diff options
| author | Luc Donnet | 2018-03-12 15:08:32 +0100 | 
|---|---|---|
| committer | GitHub | 2018-03-12 15:08:32 +0100 | 
| commit | 43a5e136c1b35483675918120405cd4f5bea3397 (patch) | |
| tree | 5faa64a1bfac1bda0152941cece46fc28279e06e | |
| parent | ba30cc2c8772dbd1934d032e2a3a6a66795df4e2 (diff) | |
| parent | c5d5eef9f107008a29536321fa0ff56209620632 (diff) | |
| download | chouette-core-43a5e136c1b35483675918120405cd4f5bea3397.tar.bz2 | |
Merge pull request #362 from af83/6133-new-exports
6133 New exports
128 files changed, 1764 insertions, 1706 deletions
| diff --git a/app/controllers/api/v1/imports_controller.rb b/app/controllers/api/v1/imports_controller.rb index 3d7f4ca79..dc2df0697 100644 --- a/app/controllers/api/v1/imports_controller.rb +++ b/app/controllers/api/v1/imports_controller.rb @@ -1,11 +1,11 @@  class Api::V1::ImportsController < Api::V1::IbooController -  defaults :resource_class => WorkbenchImport +  defaults :resource_class => Import::Workbench    belongs_to :workbench    def create      args    = workbench_import_params.merge(creator: 'Webservice')      @import = parent.workbench_imports.create(args) -    if @import.valid?  +    if @import.valid?        create!      else        render json: { status: "error", messages: @import.errors.full_messages } diff --git a/app/controllers/api/v1/internals/netex_imports_controller.rb b/app/controllers/api/v1/internals/netex_imports_controller.rb index c8e33f7b8..c2b7b20cc 100644 --- a/app/controllers/api/v1/internals/netex_imports_controller.rb +++ b/app/controllers/api/v1/internals/netex_imports_controller.rb @@ -25,13 +25,13 @@ module Api          private          def find_netex_import -          @netex_import = NetexImport.find(params[:id]) +          @netex_import = Import::Netex.find(params[:id])          rescue ActiveRecord::RecordNotFound            render json: { -            status: "error",  +            status: "error",              message: "Record not found"            } -          finish_action!    +          finish_action!          end          def find_workbench @@ -52,7 +52,7 @@ module Api            attributes = attributes.merge referential_id: @new_referential.id -          @netex_import = NetexImport.new attributes +          @netex_import = Import::Netex.new attributes            @netex_import.save!            unless @netex_import.referential diff --git a/app/controllers/api/v1/netex_imports_controller.rb b/app/controllers/api/v1/netex_imports_controller.rb index d86c1fcd8..2654fa088 100644 --- a/app/controllers/api/v1/netex_imports_controller.rb +++ b/app/controllers/api/v1/netex_imports_controller.rb @@ -34,7 +34,7 @@ module Api          attributes = attributes.merge referential_id: @new_referential.id -        @netex_import = NetexImport.new attributes +        @netex_import = Import::Netex.new attributes          @netex_import.save!          unless @netex_import.referential diff --git a/app/controllers/concerns/iev_interfaces.rb b/app/controllers/concerns/iev_interfaces.rb new file mode 100644 index 000000000..aa4d3fe6a --- /dev/null +++ b/app/controllers/concerns/iev_interfaces.rb @@ -0,0 +1,69 @@ +module IevInterfaces +  extend ActiveSupport::Concern + +  included do +    before_action only: [:index] { set_date_time_params("started_at", DateTime) } +    before_action :ransack_status_params, only: [:index] +    respond_to :html +    belongs_to :workbench +  end + +  def show +    show! do +      instance_variable_set "@#{collection_name.singularize}", resource.decorate(context: { +        workbench: @workbench +      }) +    end +  end + +  def index +    index! do |format| +      format.html { +        if collection.out_of_bounds? +          redirect_to params.merge(:page => 1) +        end +        collection = decorate_collection(collection) +      } +    end +  end + +  protected + +  def collection +    scope = parent.send(collection_name).where(parent_id: nil) +    if index_model.name.demodulize != "Base" +      scope = scope.where(type: index_model.name) +    end + +    scope = self.ransack_period_range(scope: scope, error_message:  t("#{collection_name}.filters.error_period_filter"), query: :where_started_at_in) + +    @q = scope.search(params[:q]) + +    unless instance_variable_get "@#{collection_name}" +      coll = if sort_column && sort_direction +        @q.result(distinct: true).order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: 10) +      else +        @q.result(distinct: true).order(:name).paginate(page: params[:page], per_page: 10) +      end +      instance_variable_set "@#{collection_name}", decorate_collection(coll) +    end +    instance_variable_get "@#{collection_name}" +  end + +  private +  def ransack_status_params +    if params[:q] +      return params[:q].delete(:status_eq_any) if params[:q][:status_eq_any].empty? || ( (resource_class.status.values & params[:q][:status_eq_any]).length >= 4 ) +      params[:q][:status_eq_any].push("new", "running") if params[:q][:status_eq_any].include?("pending") +      params[:q][:status_eq_any].push("aborted", "canceled") if params[:q][:status_eq_any].include?("failed") +    end +  end + +  def sort_column +    parent.imports.column_names.include?(params[:sort]) ? params[:sort] : 'created_at' +  end + +  def sort_direction +    %w[asc desc].include?(params[:direction]) ?  params[:direction] : 'desc' +  end +end diff --git a/app/controllers/export_tasks_controller.rb b/app/controllers/export_tasks_controller.rb deleted file mode 100644 index b889c1882..000000000 --- a/app/controllers/export_tasks_controller.rb +++ /dev/null @@ -1,87 +0,0 @@ -class ExportTasksController < ChouetteController -  include ReferentialSupport -  defaults :resource_class => ExportTask - -  respond_to :html, :only => [:new, :create] -  respond_to :js, :only => [:new, :create] -  belongs_to :referential - -  def new -    @available_exports = available_exports -    begin -      new! -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t(error.locale_for_error) -      redirect_to referential_path(@referential) -    end -  end - -  def create -    @available_exports = available_exports -    begin -      create! do |success, failure| -        success.html { redirect_to referential_exports_path(@referential) } -      end -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t(error.locale_for_error) -      redirect_to referential_path(@referential) -    end -  end - -  def references -    references_type = params[:filter].pluralize -    references = @referential.send(references_type).where("name ilike ?", "%#{params[:q]}%").select("id, name") -    respond_to do |format| -      format.json do -        render :json => references.collect { |child| { :id => child.id, :name => child.name } } -      end -    end -  end - -  protected - -  def available_exports -    export_task_parameters = params[:export_task] -    if export_task_parameters.present? -      @available_exports = [ -        export_task_parameters[:data_format] == "neptune" ? build_resource : NeptuneExport.new(:referential_id => @referential.id ), -        export_task_parameters[:data_format] == "netex" ? build_resource : NetexExport.new(:referential_id => @referential.id ), -        export_task_parameters[:data_format] == "gtfs" ? build_resource : GtfsExport.new(:referential_id => @referential.id ), -        export_task_parameters[:data_format] == "hub" ? build_resource : HubExport.new(:referential_id => @referential.id ), -        export_task_parameters[:data_format] == "kml" ? build_resource : KmlExport.new(:referential_id => @referential.id ) -      ] -    else -      @available_exports = [ -        NeptuneExport.new(:referential_id => @referential.id ), -        NetexExport.new(:referential_id => @referential.id ), -        GtfsExport.new(:referential_id => @referential.id ), -        HubExport.new(:referential_id => @referential.id ), -        KmlExport.new(:referential_id => @referential.id ) -      ] -    end -  end - -  def build_resource -    @export_task ||= if params[:export_task].present? -                       export_task_parameters = params[:export_task] -                       case export_task_parameters[:data_format] -                       when "neptune" -                         NeptuneExport.new(export_task_parameters) -                       when "netex" -                         NetexExport.new(export_task_parameters) -                       when "gtfs" -                         GtfsExport.new(export_task_parameters) -                       when "hub" -                         HubExport.new(export_task_parameters) -                       when "kml" -                         KmlExport.new(export_task_parameters) -                       end -                     else -                       NeptuneExport.new -                     end - -  end - -end diff --git a/app/controllers/exports_controller.rb b/app/controllers/exports_controller.rb index ccc163e34..a5282a514 100644 --- a/app/controllers/exports_controller.rb +++ b/app/controllers/exports_controller.rb @@ -1,74 +1,50 @@ -require 'will_paginate/array' -require 'open-uri' -  class ExportsController < ChouetteController -  include ReferentialSupport -  defaults :resource_class => Export - -  respond_to :html, :only => [:show, :index, :destroy, :exported_file] -  respond_to :js, :only => [:index] -  belongs_to :referential - -  def index -    begin -      index! -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t(error.locale_for_error) -      redirect_to referential_path(@referential) +  include PolicyChecker +  include RansackDateFilter +  include IevInterfaces +  skip_before_action :authenticate_user!, only: [:upload] +  defaults resource_class: Export::Base, collection_name: 'exports', instance_name: 'export' + +  def upload +    if params[:token] == resource.token_upload +      resource.file = params[:file] +      resource.save! +      redirect_to [resource.workbench, resource] +    else +      user_not_authorized      end    end -  def show -    begin -      show! -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t(error.locale_for_error) -      redirect_to referential_path(@referential) -    end -  end +  private -  def destroy -    begin -      destroy! -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t(error.locale_for_error) -      redirect_to referential_path(@referential) -    end +  def index_model +    Export::Base    end -  def exported_file -    # WARNING : files under 10kb in size get treated as StringIO by OpenUri -    # http://stackoverflow.com/questions/10496874/why-does-openuri-treat-files-under-10kb-in-size-as-stringio -    OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax') -    OpenURI::Buffer.const_set 'StringMax', 0 -    begin -      send_file open(resource.file_path), { :type => "application/#{resource.filename_extension}", :disposition => "attachment", :filename => resource.filename } -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t(error.locale_for_error) -      redirect_to referential_path(@referential) +  def build_resource +    Export::Base.force_load_descendants if Rails.env.development? +    @export ||= Export::Base.new(*resource_params) do |export| +      export.workbench = parent +      export.creator   = current_user.name      end +    @export    end -  protected - -  def export_service -    ExportService.new(@referential) -  end - -  def resource -    @export ||= export_service.find( params[:id] ) -    @line_items = @export.report.line_items -    if @line_items.size > 500 -      @line_items = @line_items.paginate(page: params[:page], per_page: 20) +  def export_params +    permitted_keys = %i(name type referential_id) +    export_class = params[:export] && params[:export][:type] && params[:export][:type].safe_constantize +    if export_class +      permitted_keys += export_class.options.keys      end -    @export +    params.require(:export).permit(permitted_keys)    end -  def collection -    @exports ||= export_service.all.sort_by{ |export| export.created_at }.reverse.paginate(:page => params[:page]) +  def decorate_collection(exports) +    ExportDecorator.decorate( +      exports, +      context: { +        workbench: @workbench +      } +    )    end  end diff --git a/app/controllers/import_messages_controller.rb b/app/controllers/import_messages_controller.rb index 4f8fe7a25..5e39445fb 100644 --- a/app/controllers/import_messages_controller.rb +++ b/app/controllers/import_messages_controller.rb @@ -1,8 +1,8 @@  class ImportMessagesController < ChouetteController -  defaults resource_class: ImportMessage, collection_name: 'import_messages', instance_name: 'import_message' +  defaults resource_class: Import::Message, collection_name: 'import_messages', instance_name: 'import_message'    respond_to :csv -  belongs_to :import, :parent_class => Import do -    belongs_to :import_resource, :parent_class => ImportResource +  belongs_to :import, :parent_class => Import::Base do +    belongs_to :import_resource, :parent_class => Import::Resource    end @@ -20,7 +20,7 @@ class ImportMessagesController < ChouetteController    end    def parent -    @import_resource ||= ImportResource.find(params[:import_resource_id]) +    @import_resource ||= Import::Resource.find(params[:import_resource_id])    end  end diff --git a/app/controllers/import_resources_controller.rb b/app/controllers/import_resources_controller.rb index ea78394a1..6d1977e0c 100644 --- a/app/controllers/import_resources_controller.rb +++ b/app/controllers/import_resources_controller.rb @@ -1,5 +1,5 @@  class ImportResourcesController < ChouetteController -  defaults resource_class: ImportResource, collection_name: 'import_resources', instance_name: 'import_resource' +  defaults resource_class: Import::Resource, collection_name: 'import_resources', instance_name: 'import_resource'    respond_to :html    belongs_to :import diff --git a/app/controllers/import_tasks_controller.rb b/app/controllers/import_tasks_controller.rb deleted file mode 100644 index 1a349087d..000000000 --- a/app/controllers/import_tasks_controller.rb +++ /dev/null @@ -1,69 +0,0 @@ -class ImportTasksController < ChouetteController -  include ReferentialSupport -  defaults :resource_class => ImportTask - -  respond_to :html, :only => [:new, :create] -  respond_to :js, :only => [:new, :create] -  belongs_to :referential - -  def new -    @available_imports = available_imports -    begin -      new! -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t('iev.exception.default') -      redirect_to referential_path(@referential) -    end -  end - -  def create -    @available_imports = available_imports -    begin -      create! do |success, failure| -        success.html { redirect_to referential_imports_path(@referential) } -      end -    rescue Ievkit::Error, Faraday::Error => error -      logger.error("Iev failure : #{error.message}") -      flash[:error] = t('iev.exception.default') -      redirect_to referential_path(@referential) -    end -  end - -  protected - -  def available_imports -    import_task_parameters = params[:import_task] - -    if import_task_parameters.present? -      @available_imports = [ -        import_task_parameters[:data_format] == "neptune" ? build_resource : NeptuneImport.new(:referential_id => @referential.id ), -        import_task_parameters[:data_format] == "netex" ? build_resource : NetexImport.new(:referential_id => @referential.id ), -        import_task_parameters[:data_format] == "gtfs" ? build_resource : GtfsImport.new(:referential_id => @referential.id ) -      ] -    else -      @available_imports = [ -        NeptuneImport.new(:referential_id => @referential.id ), -        NetexImport.new(:referential_id => @referential.id ), -        GtfsImport.new(:referential_id => @referential.id ) -      ] -    end -  end - -  def build_resource -    @import_task ||= if params[:import_task].present? -                       import_task_parameters = params[:import_task] -                       case import_task_parameters[:data_format] -                       when "neptune" -                         NeptuneImport.new(import_task_parameters) -                       when "netex" -                         @import_task = NetexImport.new(import_task_parameters) -                       when "gtfs" -                         @import_task = GtfsImport.new(import_task_parameters) -                       end -                     else -                       @import_task = NeptuneImport.new -                     end -  end - -end diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 7a999d657..8d7a723a0 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -1,31 +1,9 @@  class ImportsController < ChouetteController    include PolicyChecker    include RansackDateFilter -  before_action only: [:index] { set_date_time_params("started_at", DateTime) } +  include IevInterfaces    skip_before_action :authenticate_user!, only: [:download] -  defaults resource_class: Import, collection_name: 'imports', instance_name: 'import' -  before_action :ransack_status_params, only: [:index] -  respond_to :html -  belongs_to :workbench - -  def show -    show! do -      @import = @import.decorate(context: { -        workbench: @workbench -      }) -    end -  end - -  def index -    index! do |format| -      format.html { -        if collection.out_of_bounds? -          redirect_to params.merge(:page => 1) -        end -        @imports = decorate_imports(@imports) -      } -    end -  end +  defaults resource_class: Import::Base, collection_name: 'imports', instance_name: 'import'    def download      if params[:token] == resource.token_download @@ -35,33 +13,14 @@ class ImportsController < ChouetteController      end    end -  protected -  def collection -    scope = parent.imports.where(type: "WorkbenchImport") - -    scope = self.ransack_period_range(scope: scope, error_message:  t('imports.filters.error_period_filter'), query: :where_started_at_in) - -    @q = scope.search(params[:q]) - -    if sort_column && sort_direction -      @imports ||= @q.result(distinct: true).order(sort_column + ' ' + sort_direction).paginate(page: params[:page], per_page: 10) -    else -      @imports ||= @q.result(distinct: true).order(:name).paginate(page: params[:page], per_page: 10) -    end -  end -    private -  def ransack_status_params -    if params[:q] -      return params[:q].delete(:status_eq_any) if params[:q][:status_eq_any].empty? || ( (Import.status.values & params[:q][:status_eq_any]).length >= 4 ) -      params[:q][:status_eq_any].push("new", "running") if params[:q][:status_eq_any].include?("pending") -      params[:q][:status_eq_any].push("aborted", "canceled") if params[:q][:status_eq_any].include?("failed") -    end +  def index_model +    Import::Workbench    end - +      def build_resource -    @import ||= WorkbenchImport.new(*resource_params) do |import| +    @import ||= Import::Workbench.new(*resource_params) do |import|        import.workbench = parent        import.creator   = current_user.name      end @@ -76,14 +35,7 @@ class ImportsController < ChouetteController      )    end -  def sort_column -    parent.imports.column_names.include?(params[:sort]) ? params[:sort] : 'created_at' -  end -  def sort_direction -    %w[asc desc].include?(params[:direction]) ?  params[:direction] : 'desc' -  end - -  def decorate_imports(imports) +  def decorate_collection(imports)      ImportDecorator.decorate(        imports,        context: { diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index e38a92982..152da4fa2 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -5,7 +5,7 @@ class StatusesController < ChouetteController      status = {        referentials_blocked: Referential.blocked.count, -      imports_blocked: Import.blocked.count, +      imports_blocked: Import::Base.blocked.count,        compliance_check_sets_blocked: ComplianceCheckSet.blocked.count      }      status[:status] = global_status status diff --git a/app/decorators/export_decorator.rb b/app/decorators/export_decorator.rb new file mode 100644 index 000000000..26682f383 --- /dev/null +++ b/app/decorators/export_decorator.rb @@ -0,0 +1,21 @@ +class ExportDecorator < AF83::Decorator +  decorates Export::Base + +  set_scope { context[:workbench] } + +  define_instance_method :export_status_css_class do +    cls ='' +    cls = 'overheaded-success' if object.status == 'successful' +    cls = 'overheaded-warning' if object.status == 'warning' +    cls = 'overheaded-danger' if %w[failed aborted canceled].include? object.status +    cls +  end + +  create_action_link do |l| +    l.content t('exports.actions.new') +  end + +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link +  end +end diff --git a/app/decorators/import_decorator.rb b/app/decorators/import_decorator.rb index 1964365ae..a20dfbc42 100644 --- a/app/decorators/import_decorator.rb +++ b/app/decorators/import_decorator.rb @@ -1,5 +1,5 @@  class ImportDecorator < AF83::Decorator -  decorates Import +  decorates Import::Base    set_scope { context[:workbench] } diff --git a/app/helpers/exports_helper.rb b/app/helpers/exports_helper.rb index 8ac494cfc..4e92c7e38 100644 --- a/app/helpers/exports_helper.rb +++ b/app/helpers/exports_helper.rb @@ -1,6 +1,25 @@  # -*- coding: utf-8 -*-  module ExportsHelper -   +  def export_status status +    import_status status +  end + +  def export_option_input form, export, attr, option_def, type +    opts = { required: option_def[:required], input_html: {value: export.try(attr) || option_def[:default_value]}, as: option_def[:type], selected:  export.try(attr) || option_def[:default_value]} +    opts[:collection] = option_def[:collection] if option_def.has_key?(:collection) +    opts[:collection] = export.instance_exec(&option_def[:collection]) if option_def[:collection].is_a?(Proc) +    opts[:label] = t "activerecord.attributes.export.#{type.name.demodulize.underscore}.#{attr}" +    form.input attr, opts +  end + +  def export_message_content message +    if message.message_key == "full_text" +      message.message_attributes["text"] +    else +      t([message.class.name.underscore.gsub('/', '_').pluralize, message.message_key].join('.'), message.message_attributes.symbolize_keys) +    end +  end +    def fields_for_export_task_format(form)      begin        render :partial => export_partial_name(form), :locals => { :form => form } @@ -8,7 +27,7 @@ module ExportsHelper        ""      end    end -   +    def export_partial_name(form)      "fields_#{form.object.format.underscore}_export"    end @@ -22,7 +41,7 @@ module ExportsHelper        end.join.html_safe      end    end -   +    def compliance_icon( export_task)      return nil unless export_task.compliance_check_task      export_task.compliance_check_task.tap do |cct| @@ -33,5 +52,5 @@ module ExportsHelper        end      end    end -   +  end diff --git a/app/helpers/table_builder_helper/column.rb b/app/helpers/table_builder_helper/column.rb index ff6f2f36f..907707670 100644 --- a/app/helpers/table_builder_helper/column.rb +++ b/app/helpers/table_builder_helper/column.rb @@ -28,7 +28,9 @@ module TableBuilderHelper        return @name if @name.present?        # Transform `Chouette::Line` into "line" -      model_key = model.to_s.demodulize.underscore +      model_key = model.to_s.underscore +      model_key.gsub! 'chouette/', '' +      model_key.gsub! '/', '.'        I18n.t("activerecord.attributes.#{model_key}.#{@key}")      end diff --git a/app/javascript/packs/exports/new.js b/app/javascript/packs/exports/new.js new file mode 100644 index 000000000..ffe702cdb --- /dev/null +++ b/app/javascript/packs/exports/new.js @@ -0,0 +1,3 @@ +import MasterSlave from "../../helpers/master_slave" + +new MasterSlave("form") diff --git a/app/models/concerns/iev_interfaces/message.rb b/app/models/concerns/iev_interfaces/message.rb new file mode 100644 index 000000000..ad41e98b7 --- /dev/null +++ b/app/models/concerns/iev_interfaces/message.rb @@ -0,0 +1,9 @@ +module IevInterfaces::Message +  extend ActiveSupport::Concern + +  included do +    extend Enumerize +    enumerize :criticity, in: %i(info warning error) +    validates :criticity, presence: true +  end +end diff --git a/app/models/concerns/iev_interfaces/resource.rb b/app/models/concerns/iev_interfaces/resource.rb new file mode 100644 index 000000000..7f8c3eefd --- /dev/null +++ b/app/models/concerns/iev_interfaces/resource.rb @@ -0,0 +1,9 @@ +module IevInterfaces::Resource +  extend ActiveSupport::Concern + +  included do +    extend Enumerize +    enumerize :status, in: %i(OK ERROR WARNING IGNORED), scope: true +    validates_presence_of :name, :resource_type, :reference +  end +end diff --git a/app/models/concerns/iev_interfaces/task.rb b/app/models/concerns/iev_interfaces/task.rb new file mode 100644 index 000000000..fdd976f39 --- /dev/null +++ b/app/models/concerns/iev_interfaces/task.rb @@ -0,0 +1,119 @@ +module IevInterfaces::Task +  extend ActiveSupport::Concern + +  included do +    belongs_to :parent, polymorphic: true +    belongs_to :workbench, class_name: "::Workbench" +    belongs_to :referential + +    mount_uploader :file, ImportUploader + +    has_many :children, foreign_key: :parent_id, class_name: self.name, dependent: :destroy + +    extend Enumerize +    enumerize :status, in: %w(new pending successful warning failed running aborted canceled), scope: true, default: :new + +    validates :name, presence: true +    validates_presence_of :workbench, :creator + +    has_many :messages, class_name: messages_class_name, dependent: :destroy, foreign_key: "#{messages_class_name.split('::').first.downcase}_id" +    has_many :resources, class_name: resources_class_name, dependent: :destroy, foreign_key: "#{resources_class_name.split('::').first.downcase}_id" + +    scope :where_started_at_in, ->(period_range) do +      where('started_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end) +    end + +    scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') } + +    before_create :initialize_fields +    after_save :notify_parent +  end + +  module ClassMethods +    def launched_statuses +      %w(new pending) +    end + +    def failed_statuses +      %w(failed aborted canceled) +    end + +    def finished_statuses +      %w(successful failed warning aborted canceled) +    end + +    def abort_old +      where( +        'created_at < ? AND status NOT IN (?)', +        4.hours.ago, +        finished_statuses +      ).update_all(status: 'aborted') +    end +  end + +  def notify_parent +    return unless parent.present? +    return unless status_changed? +    parent.child_change +    t = Time.now +    self.notified_parent_at = t +    self.class.where(id: self.id).update_all notified_parent_at: t +  end + +  def children_succeedeed +    children.with_status(:successful, :warning).count +  end + +  def update_status +    status = +      if children.where(status: self.class.failed_statuses).count > 0 +        'failed' +      elsif children.where(status: "warning").count > 0 +        'warning' +      elsif children.where(status: "successful").count == children.count +        'successful' +      else +        'running' +      end + +    attributes = { +      current_step: children.count, +      status: status +    } + +    if self.class.finished_statuses.include?(status) +      attributes[:ended_at] = Time.now +    end + +    update attributes +  end + +  def child_change +    return if self.class.finished_statuses.include?(status) +    update_status +  end + +  def call_iev_callback +    return if self.class.finished_statuses.include?(status) +    threaded_call_boiv_iev +  end + +  private + +  def threaded_call_boiv_iev +    Thread.new(&method(:call_boiv_iev)) +  end + +  def call_boiv_iev +    Rails.logger.error("Begin IEV call for import") +    Net::HTTP.get iev_callback_url +    Rails.logger.error("End IEV call for import") +  rescue Exception => e +    logger.error "IEV server error : #{e.message}" +    logger.error e.backtrace.inspect +  end + +  private +  def initialize_fields +  end +end diff --git a/app/models/export.rb b/app/models/export.rb deleted file mode 100644 index 8c38d6684..000000000 --- a/app/models/export.rb +++ /dev/null @@ -1,53 +0,0 @@ -class Export -  include JobConcern - -  def initialize( response ) -    @datas = response -  end -   -  def report? -    links["action_report"].present? -  end -   -  def report -    Rails.cache.fetch("#{cache_key}/action_report", expires_in: cache_expiration) do -      report_path = links["action_report"] -      if report_path -        response = Ievkit.get(report_path) -        ExportReport.new(response) -      else -        nil -      end -    end -  end  - -  def destroy -    delete_path =  links["delete"] -    cancel_path = links["cancel"] -     -    if delete_path -      Ievkit.delete(delete_path) -    elsif cancel_path -      Ievkit.delete(cancel_path) -    else -      nil -    end -  end - -  def file_path? -    links["data"].present? -  end -   -  def file_path -    links["data"] -  end - -  def filename -    File.basename(file_path) if file_path -  end - -  def filename_extension -    File.extname(filename).gsub(".", "") if filename -  end  -   -end diff --git a/app/models/export/base.rb b/app/models/export/base.rb new file mode 100644 index 000000000..6085e0ffb --- /dev/null +++ b/app/models/export/base.rb @@ -0,0 +1,89 @@ +class Export::Base < ActiveRecord::Base +  self.table_name = "exports" + +  validates :type, presence: true + +  def self.messages_class_name +    "Export::Message" +  end + +  def self.resources_class_name +    "Export::Resource" +  end + +  def self.human_name +    I18n.t("export.#{self.name.demodulize.underscore}") +  end + +  def self.file_extension_whitelist +    %w(zip csv json) +  end + +  if Rails.env.development? +    def self.force_load_descendants +      path = Rails.root.join 'app/models/export' +      Dir.chdir path do +        Dir['**/*.rb'].each do |src| +          next if src =~ /^base/ +          klass_name = "Export::#{src[0..-4].camelize}" +          Rails.logger.info "Loading #{klass_name}" +          begin +            klass_name.constantize +          rescue => e +            Rails.logger.info "Failed: #{e.message}" +            nil +          end +        end +      end +    end +  end + +  def self.option name, opts={} +    store_accessor :options, name +    if !!opts[:required] +      validates name, presence: true +    end +    @options ||= {} +    @options[name] = opts +  end + +  def self.options +    @options ||= {} +  end + +  include IevInterfaces::Task + +  def self.model_name +    ActiveModel::Name.new Export::Base, Export::Base, "Export" +  end + +  def self.user_visible_descendants +    descendants.select &:user_visible? +  end + +  def self.user_visible? +    true +  end + +  def visible_options +    options.select{|k, v| ! k.match  /^_/} +  end + +  def display_option_value option_name, context +    option = self.class.options[option_name.to_sym] +    val = self.options[option_name.to_s] +    if option[:display] +      context.instance_exec(val, &option[:display]) +    else +      val +    end +  end + +  private + +  def initialize_fields +    super +    self.token_upload = SecureRandom.urlsafe_base64 +  end + +end diff --git a/app/models/export/message.rb b/app/models/export/message.rb new file mode 100644 index 000000000..b64b524ac --- /dev/null +++ b/app/models/export/message.rb @@ -0,0 +1,8 @@ +class Export::Message < ActiveRecord::Base +  self.table_name = :export_messages + +  include IevInterfaces::Message + +  belongs_to :export, class_name: Export::Base +  belongs_to :resource, class_name: Export::Resource +end diff --git a/app/models/export/netex.rb b/app/models/export/netex.rb new file mode 100644 index 000000000..069ec2209 --- /dev/null +++ b/app/models/export/netex.rb @@ -0,0 +1,22 @@ +class Export::Netex < Export::Base +  after_commit :call_iev_callback, on: :create +  option :export_type, collection: %w(line full), required: true +  option :duration, type: :integer, default_value: 90, required: true +  option :line_code + +  private + +  def iev_callback_url +    URI("#{Rails.configuration.iev_url}/boiv_iev/referentials/exporter/new?id=#{id}") +  end + +  # def self.user_visible? +  #   false +  # end + +  def destroy_non_ready_referential +    if referential && !referential.ready +      referential.destroy +    end +  end +end diff --git a/app/models/export/referential_companies.rb b/app/models/export/referential_companies.rb new file mode 100644 index 000000000..0b6187060 --- /dev/null +++ b/app/models/export/referential_companies.rb @@ -0,0 +1,92 @@ +class Export::ReferentialCompanies < Export::Base +  option :referential_id, +    type: :select, +    collection: ->(){workbench.referentials.all}, +    required: true, +    display: ->(val){r = Referential.find(val); link_to(r.name, [r])} + +  after_create :call_exporter_async + +  def referential +    Referential.find referential_id +  end + +  def call_exporter_async +    SimpleExportWorker.perform_async(id) +  end + +  def exporter +    SimpleExporter.define :referential_companies do |config| +      config.separator = ";" +      config.encoding = 'ISO-8859-1' +      config.add_column :name +      config.add_column :registration_number +    end + +    @exporter ||= begin +      if options[:_exporter_id] +        exporter = SimpleExporter.find options[:exporter_id] +      else +        exporter = SimpleExporter.create configuration_name: :referential_companies +        options[:_exporter_id] = exporter.id +      end +      exporter +    end +  end + +  def call_exporter +    tmp = Tempfile.new ["referential_companies", ".csv"] +    referential.switch +    exporter.configure do |config| +      config.collection = referential.companies.order(:name) +    end +    exporter.filepath = tmp.path +    exporter.export +    set_status_from_exporter +    convert_exporter_journal_to_messages +    self.file = tmp +    self.save! +  end + +  def set_status_from_exporter +    if exporter.status == :error +      self.status = :failed +    else +      if exporter.status == :success +        self.status = :successful +      else +        self.status = :warning +      end +    end +  end + +  def convert_exporter_journal_to_messages +    self.messages.destroy_all +    exporter.journal.each do |journal_item| +      journal_item.symbolize_keys! +      vals = {} + +      if journal_item[:kind].to_s == "warning" +        vals[:criticity] = :warning +      elsif journal_item[:kind].to_s == "error" +        vals[:criticity] = :error +      else +        vals[:criticity] = :info +        if journal_item[:event].to_s == "success" +          vals[:message_key] = :success +        end +      end +      vals[:resource_attributes] = journal_item[:row] + +      if journal_item[:message].present? +        vals[:message_key] = :full_text +        vals[:message_attributes] = { +          text: journal_item[:message] +        } +      end +      vals[:message_attributes] ||= {} +      vals[:message_attributes][:line] =  journal_item[:line] +      self.messages.build vals +    end +  end +end diff --git a/app/models/export/resource.rb b/app/models/export/resource.rb new file mode 100644 index 000000000..98f103be4 --- /dev/null +++ b/app/models/export/resource.rb @@ -0,0 +1,8 @@ +class Export::Resource < ActiveRecord::Base +  self.table_name = :export_resources + +  include IevInterfaces::Resource + +  belongs_to :export, class_name: Export::Base +  has_many :messages, class_name: "ExportMessage", foreign_key: :resource_id +end diff --git a/app/models/export/workgroup.rb b/app/models/export/workgroup.rb new file mode 100644 index 000000000..3430596c7 --- /dev/null +++ b/app/models/export/workgroup.rb @@ -0,0 +1,9 @@ +class Export::Workgroup < Export::Base +  after_commit :launch_worker, :on => :create + +  option :duration, required: true, type: :integer, default_value: 90 + +  def launch_worker +    WorkgroupExportWorker.perform_async(id) +  end +end diff --git a/app/models/export_log_message.rb b/app/models/export_log_message.rb deleted file mode 100644 index 4bb9d3cc7..000000000 --- a/app/models/export_log_message.rb +++ /dev/null @@ -1,42 +0,0 @@ -class ExportLogMessage < ActiveRecord::Base -  belongs_to :export - -  acts_as_list :scope => :export -   -  validates_presence_of :key -  validates_inclusion_of :severity, :in => %w{info warning error ok uncheck fatal} - -  def arguments=(arguments) -    write_attribute :arguments, (arguments.to_json if arguments.present?) -  end - -  def arguments -    @decoded_arguments ||=  -      begin -        if (stored_arguments = raw_attributes).present? -          ActiveSupport::JSON.decode stored_arguments -        else -          {} -        end -      end -  end - -  def raw_attributes -    read_attribute(:arguments) -  end - -  before_validation :define_default_attributes, :on => :create -  def define_default_attributes -    self.severity ||= "info" -  end -  -  def full_message -    last_key=key.rpartition("|").last -    begin -      I18n.translate last_key, arguments.symbolize_keys.merge(:scope => "export_log_messages.messages").merge(:default => :undefined).merge(:key => last_key) -    rescue => e -      Rails.logger.error "missing arguments for message "+last_key -      I18n.translate "WRONG_DATA",{"0"=>last_key}.symbolize_keys.merge(:scope => "export_log_messages.messages").merge(:default => :undefined).merge(:key => "WRONG_DATA") -    end -  end -end diff --git a/app/models/export_report.rb b/app/models/export_report.rb deleted file mode 100644 index 3c0788106..000000000 --- a/app/models/export_report.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ExportReport -  #include ReportConcern - -  def initialize( response ) -    @datas = response.action_report -  end - -end diff --git a/app/models/export_service.rb b/app/models/export_service.rb deleted file mode 100644 index 2dbe0d7b3..000000000 --- a/app/models/export_service.rb +++ /dev/null @@ -1,23 +0,0 @@ -class ExportService -   -  attr_reader :referential -   -  def initialize(referential) -    @referential = referential -  end - -  # Find an export whith his id -  def find(id) -    Export.new( Ievkit.scheduled_job(referential.slug, id, { :action => "exporter" }) ) -  end - -  # Find all exports -  def all -    [].tap do |jobs| -      Ievkit.jobs(referential.slug, { :action => "exporter" }).each do |job| -        jobs << Export.new( job ) -      end -    end -  end - -end diff --git a/app/models/export_task.rb b/app/models/export_task.rb deleted file mode 100644 index f02cb914e..000000000 --- a/app/models/export_task.rb +++ /dev/null @@ -1,119 +0,0 @@ -class ExportTask -  extend Enumerize -  extend ActiveModel::Naming -  extend ActiveModel::Translation -  extend ActiveModel::Callbacks -  include ActiveModel::Validations -  include ActiveModel::Conversion - -  attr_accessor :start_date, :end_date - -  define_model_callbacks :initialize, only: :after - -  enumerize :data_format, in: %w( neptune netex gtfs hub kml ) -  attr_accessor :referential_id, :user_id, :user_name, :references_type, :data_format, :name, :projection_type, :reference_ids - -  validates_presence_of :referential_id -  validates_presence_of :user_id -  validates_presence_of :user_name -  validates_presence_of :name -  validates_presence_of :data_format - -  validate :period_validation - -  after_initialize :init_period - -  def initialize( params = {} ) -    run_callbacks :initialize do -      params.each {|k,v| send("#{k}=",v)} -    end -  end - -  def period_validation -    st_date = start_date.is_a?(String) ? Date.parse(start_date) : start_date -    ed_date = end_date.is_a?(String) ? Date.parse(end_date) : end_date - -    unless  Chouette::TimeTable.start_validity_period.nil? || st_date.nil? -      tt_st_date = Chouette::TimeTable.start_validity_period -      errors.add(:start_date, ExportTask.human_attribute_name("start_date_greater_than" , {:tt_st_date => tt_st_date})) unless tt_st_date <= st_date -    end -    unless st_date.nil? || ed_date.nil? -      errors.add(:end_date, ExportTask.human_attribute_name("end_date_greater_than_start_date")) unless st_date <= ed_date -    end -    unless  ed_date.nil? || Chouette::TimeTable.end_validity_period.nil? -      tt_ed_date = Chouette::TimeTable.end_validity_period -      errors.add(:end_date, ExportTask.human_attribute_name("end_date_less_than", {:tt_ed_date => tt_ed_date})) unless ed_date <= tt_ed_date -    end -  end - -  def init_period -    unless Chouette::TimeTable.start_validity_period.nil? -      if start_date.nil? -        self.start_date = Chouette::TimeTable.start_validity_period -      end -      if end_date.nil? -        self.end_date = Chouette::TimeTable.end_validity_period -      end -    end -  end - -  def referential -    Referential.find(referential_id) -  end - -  def organisation -    referential.organisation -  end - -  def save -    if self.valid? -      # Call Iev Server -      begin -        Ievkit.create_job( referential.slug, "exporter", data_format, { -                             :file1 => params_io, -                           } ) -      rescue Exception => exception -        raise exception -      end -      true -    else -      false -    end -  end - -  def self.data_formats -    self.data_format.values -  end - -  def self.references_types -    self.references_type.values -  end - -  def params -    {}.tap do |h| -      h["parameters"] = action_params -    end -  end - -  def action_params -    {} -  end - -  def params_io -    file = StringIO.new( params.to_json ) -    Faraday::UploadIO.new(file, "application/json", "parameters.json") -  end - -  def self.optional_attributes(references_type) -    [] -  end - -  def optional_attributes -    self.class.optional_attributes(references_type.to_s) -  end - -  def optional_attribute?(attribute) -    optional_attributes.include? attribute.to_sym -  end - -end diff --git a/app/models/gtfs_export.rb b/app/models/gtfs_export.rb deleted file mode 100644 index d0b9fc4f9..000000000 --- a/app/models/gtfs_export.rb +++ /dev/null @@ -1,47 +0,0 @@ -class GtfsExport < ExportTask - -  validates_presence_of :time_zone, unless: Proc.new { |e| e.optional_attribute? :time_zone } -  attr_accessor :object_id_prefix, :time_zone - -  enumerize :references_type, in: %w( network line company group_of_line stop_area ) - -  after_initialize :init_params - -  def init_params -    if time_zone.nil? -      self.time_zone = "Paris" -    end -  end - -  def real_time_zone -    ActiveSupport::TimeZone.find_tzinfo(time_zone).name -  end - -  def action_params -    { -      "gtfs-export" => { -        "name" => name, -        "references_type" => references_type, -        "reference_ids" => reference_ids, -        "user_name" => user_name, -        "organisation_name" => organisation.name, -        "referential_name" => referential.name, -        "time_zone" => real_time_zone, -        "object_id_prefix" => object_id_prefix, -        "start_date" => start_date, -        "end_date" => end_date -      } -    } -  end - -  def self.optional_attributes(references_type) -    super.tap do |optional_attributes| -      optional_attributes.push :time_zone, :start_date, :end_date if references_type == "stop_area" -    end -  end - -  def data_format -    "gtfs" -  end - -end diff --git a/app/models/hub_export.rb b/app/models/hub_export.rb deleted file mode 100644 index 802600692..000000000 --- a/app/models/hub_export.rb +++ /dev/null @@ -1,24 +0,0 @@ -class HubExport < ExportTask - -  enumerize :references_type, in: %w( network line company group_of_line ) - -  def action_params -    { -      "hub-export" => { -        "name" => name, -        "references_type" => references_type, -        "reference_ids" => reference_ids, -        "user_name" => user_name, -        "organisation_name" => organisation.name, -        "referential_name" => referential.name, -        "start_date" => start_date, -        "end_date" => end_date -      } -    } -  end -   -  def data_format -    "hub" -  end  - -end diff --git a/app/models/import.rb b/app/models/import.rb deleted file mode 100644 index 29aadcd56..000000000 --- a/app/models/import.rb +++ /dev/null @@ -1,103 +0,0 @@ -class Import < ActiveRecord::Base -  mount_uploader :file, ImportUploader -  belongs_to :workbench -  belongs_to :referential - -  belongs_to :parent, polymorphic: true - -  has_many :messages, class_name: "ImportMessage", dependent: :destroy -  has_many :resources, class_name: "ImportResource", dependent: :destroy -  has_many :children, foreign_key: :parent_id, class_name: "Import", dependent: :destroy - -  scope :where_started_at_in, ->(period_range) do -    where('started_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end) -   end - -  scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') } - -  extend Enumerize -  enumerize :status, in: %w(new pending successful warning failed running aborted canceled), scope: true, default: :new - -  validates :name, presence: true -  validates :file, presence: true -  validates_presence_of :workbench, :creator - -  before_create :initialize_fields - -  def self.model_name -    ActiveModel::Name.new Import, Import, "Import" -  end - -  def children_succeedeed -    children.with_status(:successful, :warning).count -  end - -  def self.launched_statuses -    %w(new pending) -  end - -  def self.failed_statuses -    %w(failed aborted canceled) -  end - -  def self.finished_statuses -    %w(successful failed warning aborted canceled) -  end - -  def self.abort_old -    where( -      'created_at < ? AND status NOT IN (?)', -      4.hours.ago, -      finished_statuses -    ).update_all(status: 'aborted') -  end - -  def notify_parent -    parent.child_change -    update(notified_parent_at: DateTime.now) -  end - -  def child_change -    return if self.class.finished_statuses.include?(status) - -    update_status -    update_referentials -  end - -  def update_status -    status = -      if children.where(status: self.class.failed_statuses).count > 0 -        'failed' -      elsif children.where(status: "warning").count > 0 -        'warning' -      elsif children.where(status: "successful").count == children.count -        'successful' -      end - -    attributes = { -      current_step: children.count, -      status: status -    } - -    if self.class.finished_statuses.include?(status) -      attributes[:ended_at] = Time.now -    end - -    update attributes -  end - -  def update_referentials -    return unless self.class.finished_statuses.include?(status) - -    children.each do |import| -      import.referential.update(ready: true) if import.referential -    end -  end - -  private - -  def initialize_fields -    self.token_download = SecureRandom.urlsafe_base64 -  end - -end diff --git a/app/models/import/base.rb b/app/models/import/base.rb new file mode 100644 index 000000000..1dd9c4195 --- /dev/null +++ b/app/models/import/base.rb @@ -0,0 +1,45 @@ +class Import::Base < ActiveRecord::Base +  self.table_name = "imports" +  validates :file, presence: true + +  def self.messages_class_name +    "Import::Message" +  end + +  def self.resources_class_name +    "Import::Resource" +  end + +  def self.file_extension_whitelist +    %w(zip) +  end + +  include IevInterfaces::Task + +  def self.model_name +    ActiveModel::Name.new Import::Base, Import::Base, "Import" +  end + +  def child_change +    return if self.class.finished_statuses.include?(status) + +    super +    update_referentials +  end + +  def update_referentials +    return unless self.class.finished_statuses.include?(status) + +    children.each do |import| +      import.referential.update(ready: true) if import.referential +    end +  end + +  private + +  def initialize_fields +    super +    self.token_download = SecureRandom.urlsafe_base64 +  end + +end diff --git a/app/models/gtfs_import.rb b/app/models/import/gtfs.rb index d09ca4cb3..03cf49e60 100644 --- a/app/models/gtfs_import.rb +++ b/app/models/import/gtfs.rb @@ -1,5 +1,5 @@  require 'net/http' -class GtfsImport < Import +class Import::Gtfs < Import::Base    before_destroy :destroy_non_ready_referential    after_commit :launch_java_import, on: :create diff --git a/app/models/import/message.rb b/app/models/import/message.rb new file mode 100644 index 000000000..c1900a718 --- /dev/null +++ b/app/models/import/message.rb @@ -0,0 +1,8 @@ +class Import::Message < ActiveRecord::Base +  self.table_name = :import_messages + +  include IevInterfaces::Message + +  belongs_to :import, class_name: Import::Base +  belongs_to :resource, class_name: Import::Resource +end diff --git a/app/models/import/netex.rb b/app/models/import/netex.rb new file mode 100644 index 000000000..2b0982229 --- /dev/null +++ b/app/models/import/netex.rb @@ -0,0 +1,24 @@ +require 'net/http' +class Import::Netex < Import::Base +  before_destroy :destroy_non_ready_referential + +  after_commit :call_iev_callback, on: :create + +  before_save def abort_unless_referential +    self.status = 'aborted' unless referential +  end + +  validates_presence_of :parent + +  private + +  def iev_callback_url +    URI("#{Rails.configuration.iev_url}/boiv_iev/referentials/importer/new?id=#{id}") +  end + +  def destroy_non_ready_referential +    if referential && !referential.ready +      referential.destroy +    end +  end +end diff --git a/app/models/import/resource.rb b/app/models/import/resource.rb new file mode 100644 index 000000000..73be04d0e --- /dev/null +++ b/app/models/import/resource.rb @@ -0,0 +1,8 @@ +class Import::Resource < ActiveRecord::Base +  self.table_name = :import_resources + +  include IevInterfaces::Resource + +  belongs_to :import, class_name: Import::Base +  has_many :messages, class_name: "ImportMessage", foreign_key: :resource_id +end diff --git a/app/models/workbench_import.rb b/app/models/import/workbench.rb index 27f53a44f..f6e15cb89 100644 --- a/app/models/workbench_import.rb +++ b/app/models/import/workbench.rb @@ -1,4 +1,4 @@ -class WorkbenchImport < Import +class Import::Workbench < Import::Base    after_commit :launch_worker, :on => :create    def launch_worker diff --git a/app/models/import_message.rb b/app/models/import_message.rb deleted file mode 100644 index de70c35d1..000000000 --- a/app/models/import_message.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ImportMessage < ActiveRecord::Base -  extend Enumerize -  belongs_to :import -  belongs_to :resource, class_name: ImportResource -  enumerize :criticity, in: %i(info warning error) - -  validates :criticity, presence: true -end diff --git a/app/models/import_message_export.rb b/app/models/import_message_export.rb deleted file mode 100644 index 991eb0f61..000000000 --- a/app/models/import_message_export.rb +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -require "csv" -require "zip" - -class ImportMessageExport -  include ActiveModel::Validations -  include ActiveModel::Conversion -  extend  ActiveModel::Naming - -  attr_accessor :import_messages - -  def initialize(attributes = {}) -    attributes.each { |name, value| send("#{name}=", value) } -  end - -  def persisted? -    false -  end - -  def label(name) -    I18n.t "vehicle_journey_exports.label.#{name}" -  end - -  def column_names -    ["criticity", "message key", "message", "file name", "line", "column"] -  end - -  def to_csv(options = {}) -    csv_string = CSV.generate(options) do |csv| -      csv << column_names -      import_messages.each do |import_message| -        csv << [import_message.criticity, import_message.message_key, I18n.t("import_messages.#{import_message.message_key}", import_message.message_attributes.deep_symbolize_keys), *import_message.resource_attributes.values_at("filename", "line_number", "column_number")  ] -      end -    end -    # We add a BOM to indicate we use UTF-8 -    "\uFEFF" + csv_string -  end - -  def to_zip(temp_file,options = {}) -    ::Zip::OutputStream.open(temp_file) { |zos| } -    ::Zip::File.open(temp_file.path, ::Zip::File::CREATE) do |zipfile| -      zipfile.get_output_stream(label("vj_filename")+route.id.to_s+".csv") { |f| f.puts to_csv(options) } -      zipfile.get_output_stream(label("tt_filename")+".csv") { |f| f.puts time_tables_to_csv(options) } -      zipfile.get_output_stream(label("ftn_filename")+".csv") { |f| f.puts footnotes_to_csv(options) } -    end -  end - -end diff --git a/app/models/import_report.rb b/app/models/import_report.rb deleted file mode 100644 index ba13f0118..000000000 --- a/app/models/import_report.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ImportReport -  #include ReportConcern - -  def initialize( response ) -    @datas = response.action_report -  end - -end diff --git a/app/models/import_resource.rb b/app/models/import_resource.rb deleted file mode 100644 index 55e752e74..000000000 --- a/app/models/import_resource.rb +++ /dev/null @@ -1,11 +0,0 @@ -class ImportResource < ActiveRecord::Base -  belongs_to :import - -  extend Enumerize -  enumerize :status, in: %i(OK ERROR WARNING IGNORED), scope: true - -  validates_presence_of :name, :resource_type, :reference - -  has_many :messages, class_name: "ImportMessage", foreign_key: :resource_id - -end diff --git a/app/models/import_service.rb b/app/models/import_service.rb deleted file mode 100644 index 2e3c1012b..000000000 --- a/app/models/import_service.rb +++ /dev/null @@ -1,23 +0,0 @@ -class ImportService - -  attr_reader :referential -   -  def initialize( referential ) -    @referential = referential -  end - -  # Find an import whith his id -  def find(id) -    Import.new( Ievkit.scheduled_job(referential.slug, id, { :action => "importer" }) ) -  end - -  # Find all imports -  def all -    [].tap do |jobs| -      Ievkit.jobs(referential.slug, { :action => "importer" }).each do |job| -        jobs << Import.new( job ) -      end -    end -  end -   -end diff --git a/app/models/import_task.rb b/app/models/import_task.rb deleted file mode 100644 index 7dfa2c644..000000000 --- a/app/models/import_task.rb +++ /dev/null @@ -1,141 +0,0 @@ -require "zip" - -class ImportTask -  extend Enumerize -  extend ActiveModel::Naming -  extend ActiveModel::Translation -  include ActiveModel::Validations -  include ActiveModel::Conversion - -  # TODO : Move in configuration -  @@root = "#{Rails.root}/tmp/imports" -  cattr_accessor :root - -  enumerize :data_format, in: %w( neptune netex gtfs ) -  attr_accessor :referential_id, :user_id, :user_name, :data_format, :resources, :name, :no_save - -  validates_presence_of :referential_id -  validates_presence_of :resources -  validates_presence_of :user_id -  validates_presence_of :user_name -  validates_presence_of :name - -  validate :validate_file_size, :validate_file_content - -  def initialize( params = {} ) -    params.each {|k,v| send("#{k}=",v)} -  end - -  def referential -    Referential.find(referential_id) -  end - -  def organisation -    referential.organisation -  end - -  def save -    if valid? -      # Save resources -      save_resources - -      # Call Iev Server -      begin -        Ievkit.create_job(referential.slug, "importer", data_format, { -                            :file1 => params_io, -                            :file2 => transport_data_io -                          } - -                         ) - -        # Delete resources -        delete_resources -      rescue Exception => exception -        # If iev server has an error must delete resources before -        delete_resources - -        raise exception -      end -      true -    else -      false -    end -  end - -  def params -    {}.tap do |h| -      h["parameters"] = {} -    end -  end - -  def self.data_formats -    self.data_format.values -  end - -  def params_io -    file = StringIO.new( params.to_json ) -    Faraday::UploadIO.new(file, "application/json", "parameters.json") -  end - -  def transport_data_io -    file = File.new(saved_resources_path, "r") -    if file_extname == ".zip" -      Faraday::UploadIO.new(file, "application/zip", original_filename ) -    elsif file_extname == ".xml" -      Faraday::UploadIO.new(file, "application/xml", original_filename ) -    end -  end - -  def save_resources -    FileUtils.mkdir_p root -    FileUtils.cp resources.path, saved_resources_path -  end - -  def delete_resources -    FileUtils.rm saved_resources_path if File.exists? saved_resources_path -  end - -  def original_filename -    resources.original_filename -  end - -  def file_extname -    File.extname(original_filename) if original_filename -  end - -  def saved_resources_path -    @saved_resources_path ||= "#{root}/#{Time.now.to_i}#{file_extname}" -  end - -  @@maximum_file_size = 80.megabytes -  cattr_accessor :maximum_file_size - -  def validate_file_size -    return unless resources.present? and resources.path.present? and File.exists? resources.path - -    if File.size(resources.path) > maximum_file_size -      message = I18n.t("activemodel.errors.models.import_task.attributes.resources.maximum_file_size", file_size:   ActionController::Base.helpers.number_to_human_size(File.size(resources.path)), maximum_file_size: ActionController::Base.helpers.number_to_human_size(maximum_file_size)) -      errors.add(:resources, message) -    end -  end - -  @@valid_mime_types = { -    neptune: %w{application/zip application/xml}, -    netex: %w{application/zip}, -    gtfs: %w{application/zip  text/plain} -  } -  cattr_accessor :valid_mime_types - -  def validate_file_content -    return unless resources.present? and resources.path.present? and File.exists? resources.path - -    mime_type = (File.open(resources.path) { |f| MimeMagic.by_magic f }).try :type -    expected_mime_types = valid_mime_types[data_format.to_sym] - -    unless expected_mime_types.include? mime_type -      message = I18n.t("activemodel.errors.models.import_task.attributes.resources.invalid_mime_type", mime_type: mime_type) -      errors.add(:resources, message) -    end -  end - -end diff --git a/app/models/kml_export.rb b/app/models/kml_export.rb deleted file mode 100644 index f6db77172..000000000 --- a/app/models/kml_export.rb +++ /dev/null @@ -1,24 +0,0 @@ -class KmlExport < ExportTask - -  enumerize :references_type, in: %w( network line company group_of_line ) - -  def action_params -    { -      "kml-export" => { -        "name" => name, -        "references_type" => references_type, -        "reference_ids" => reference_ids, -        "user_name" => user_name, -        "organisation_name" => organisation.name, -        "referential_name" => referential.name, -        "start_date" => start_date, -        "end_date" => end_date -      } -    } -  end -   -  def data_format -    "kml" -  end - -end diff --git a/app/models/neptune_export.rb b/app/models/neptune_export.rb deleted file mode 100644 index f25db69c0..000000000 --- a/app/models/neptune_export.rb +++ /dev/null @@ -1,27 +0,0 @@ -class NeptuneExport < ExportTask - -  attr_accessor :extensions, :export_type -  enumerize :references_type, in: %w( network line company group_of_line ) -   -  def action_params -    { -      "neptune-export" => { -        "name" => name, -        "references_type" => references_type, -        "reference_ids" => reference_ids, -        "user_name" => user_name, -        "organisation_name" => organisation.name, -        "referential_name" => referential.name, -        "projection_type" => projection_type || "", -        "add_extension" => extensions, -        "start_date" => start_date, -        "end_date" => end_date -      } -    } -  end - -  def data_format -    "neptune" -  end - -end diff --git a/app/models/neptune_import.rb b/app/models/neptune_import.rb deleted file mode 100644 index 1f0bdaa13..000000000 --- a/app/models/neptune_import.rb +++ /dev/null @@ -1,19 +0,0 @@ -class NeptuneImport < ImportTask -   -  def action_params   -    {  -      "neptune-import" => { -        "no_save" => no_save, -        "user_name" => user_name, -        "name" => name, -        "organisation_name" => organisation.name, -        "referential_name" => referential.name, -      } -    } -  end -     -  def data_format -    "neptune" -  end - -end diff --git a/app/models/netex_export.rb b/app/models/netex_export.rb deleted file mode 100644 index a4c3e2454..000000000 --- a/app/models/netex_export.rb +++ /dev/null @@ -1,24 +0,0 @@ -class NetexExport < ExportTask - -  enumerize :references_type, in: %w( network line company group_of_line ) - -  def action_params -    {      -      "netex-export" => { -        "name" => name, -        "references_type" => references_type, -        "reference_ids" => reference_ids, -        "user_name" => user_name, -        "organisation_name" => organisation.name, -        "referential_name" => referential.name, -        "start_date" => start_date, -        "end_date" => end_date -      } -    } -  end -   -  def data_format -    "netex" -  end - -end diff --git a/app/models/netex_import.rb b/app/models/netex_import.rb deleted file mode 100644 index b21af3408..000000000 --- a/app/models/netex_import.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'net/http' -class NetexImport < Import -  before_destroy :destroy_non_ready_referential - -  after_commit :launch_java_import, on: :create -  before_save def abort_unless_referential -    self.status = 'aborted' unless referential -  end - -  validates_presence_of :parent - -  def launch_java_import -    return if self.class.finished_statuses.include?(status) -    threaded_call_boiv_iev -  end - -  private - -  def destroy_non_ready_referential -    if referential && !referential.ready -      referential.destroy -    end -  end - -  def threaded_call_boiv_iev -    Thread.new(&method(:call_boiv_iev)) -  end - -  def call_boiv_iev -    Rails.logger.error("Begin IEV call for import") -    Net::HTTP.get(URI("#{Rails.configuration.iev_url}/boiv_iev/referentials/importer/new?id=#{id}")) -    Rails.logger.error("End IEV call for import") -  rescue Exception => e -    logger.error "IEV server error : #{e.message}" -    logger.error e.backtrace.inspect -  end - -end diff --git a/app/models/simple_interface.rb b/app/models/simple_interface.rb index 489419482..11b9d0dcd 100644 --- a/app/models/simple_interface.rb +++ b/app/models/simple_interface.rb @@ -16,7 +16,7 @@ class SimpleInterface < ActiveRecord::Base      def find_configuration name        @importers ||= {}        configuration = @importers[name.to_sym] -      raise "Importer not found: #{name}" unless configuration +      raise "#{self.name} not found: #{name}" unless configuration        configuration      end    end @@ -27,6 +27,10 @@ class SimpleInterface < ActiveRecord::Base      self.journal ||= []    end +  def configuration +    @configuration ||= self.class.find_configuration self.configuration_name +  end +    def init_env opts      @verbose = opts.delete :verbose diff --git a/app/models/workbench.rb b/app/models/workbench.rb index b6f90c7dc..b5f4673bb 100644 --- a/app/models/workbench.rb +++ b/app/models/workbench.rb @@ -13,8 +13,9 @@ class Workbench < ActiveRecord::Base    has_many :companies, through: :line_referential    has_many :group_of_lines, through: :line_referential    has_many :stop_areas, through: :stop_area_referential -  has_many :imports -  has_many :workbench_imports +  has_many :imports, class_name: Import::Base +  has_many :exports, class_name: Export::Base +  has_many :workbench_imports, class_name: Import::Workbench    has_many :compliance_check_sets    has_many :compliance_control_sets    has_many :merges diff --git a/app/policies/export_policy.rb b/app/policies/export_policy.rb new file mode 100644 index 000000000..e667f3207 --- /dev/null +++ b/app/policies/export_policy.rb @@ -0,0 +1,15 @@ +class ExportPolicy < ApplicationPolicy +  class Scope < Scope +    def resolve +      scope +    end +  end + +  def create? +    user.has_permission?('exports.create') +  end + +  def update? +    user.has_permission?('exports.update') +  end +end diff --git a/app/uploaders/import_uploader.rb b/app/uploaders/import_uploader.rb index 60e17ca0f..3491768f6 100644 --- a/app/uploaders/import_uploader.rb +++ b/app/uploaders/import_uploader.rb @@ -37,7 +37,7 @@ class ImportUploader < CarrierWave::Uploader::Base    # Add a white list of extensions which are allowed to be uploaded.    # For images you might use something like this:    def extension_whitelist -    %w(zip) +    model.class.try(:file_extension_whitelist) || %w(zip)    end    # Override the filename of the uploaded files: diff --git a/app/views/compliance_control_sets/_filters.html.slim b/app/views/compliance_control_sets/_filters.html.slim index 4348defac..5cf282559 100644 --- a/app/views/compliance_control_sets/_filters.html.slim +++ b/app/views/compliance_control_sets/_filters.html.slim @@ -11,7 +11,7 @@        = f.input :organisation_name_eq_any, collection: organisations_filters_values, as: :check_boxes, label: false, label_method: lambda {|w| ("<span>#{w.name}</span>").html_safe}, required: false, wrapper_html: {class: 'checkbox_list'}      .form-group.togglable class=filter_item_class(params[:q], :updated_at) -      = f.label Import.human_attribute_name(:updated_at), required: false, class: 'control-label' +      = f.label Import::Base.human_attribute_name(:updated_at), required: false, class: 'control-label'        .filter_menu          = f.simple_fields_for :updated_at do |p|            = p.input :start_date, as: :date, label: false, wrapper_html: {class: 'date smart_date filter_menu-item'}, default: @begin_range, include_blank: @begin_range ? false : true diff --git a/app/views/exports/_export.html.slim b/app/views/exports/_export.html.slim deleted file mode 100644 index f1f7e9753..000000000 --- a/app/views/exports/_export.html.slim +++ /dev/null @@ -1,20 +0,0 @@ -#index_item.panel.panel-default.export -  .panel-heading -    .panel-title.clearfix -      span.pull-right -        = link_to referential_export_path(@referential, export.id), :method => :delete, :data => {:confirm =>  t('exports.actions.destroy_confirm')}, class: 'btn btn-danger btn-sm' do -          span.fa.fa-trash-o -       -      h5 -      	= link_to( referential_export_path(@referential, export.id), :class => "preview", :title => "#{Export.model_name.human.capitalize} #{export.name}") do -      	  = job_status_title(export) - -  .panel-body -    p -      = link_to( font_awesome_classic_tag("fa-file-#{export.filename_extension}-o") + t("exports.show.exported_file"), exported_file_referential_export_path(@referential, export.id) ) if export.file_path - -  .panel-footer -    = export_attributes_tag(export) -    .history -      = l export.created_at, :format => "%d/%m/%Y %H:%M" -      = " | #{export.user_name}"
\ No newline at end of file diff --git a/app/views/exports/_exports.html.slim b/app/views/exports/_exports.html.slim deleted file mode 100644 index 7a0461def..000000000 --- a/app/views/exports/_exports.html.slim +++ /dev/null @@ -1,9 +0,0 @@ -.page_info -  span.search = t("will_paginate.page_entries_info.search") -  = page_entries_info @exports - -.exports.paginated_content -  = paginated_content @exports, "exports/export" - -.pagination -  = will_paginate @exports, :container => false, renderer: RemoteBootstrapPaginationLinkRenderer
\ No newline at end of file diff --git a/app/views/exports/_form.html.slim b/app/views/exports/_form.html.slim new file mode 100644 index 000000000..7817fdf1a --- /dev/null +++ b/app/views/exports/_form.html.slim @@ -0,0 +1,16 @@ += simple_form_for export, as: :export, url: workbench_exports_path(workbench), html: {class: 'form-horizontal', id: 'wb_export_form'}, wrapper: :horizontal_form do |form| + +  .row +    .col-lg-12 +      = form.input :name +    .col-lg-12 +      = form.input :type, as: :select, collection: Export::Base.user_visible_descendants, label_method: :human_name + +      - Export::Base.user_visible_descendants.each do |child| +        .slave data-master="[name='export[type]']" data-value=child.name +          - child.options.each do |attr, option_def| +            = export_option_input form, export, attr, option_def, child + +  = form.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'wb_export_form' + += javascript_pack_tag "exports/new" diff --git a/app/views/exports/index.html.slim b/app/views/exports/index.html.slim index bbcb2a5a7..f97b07231 100644 --- a/app/views/exports/index.html.slim +++ b/app/views/exports/index.html.slim @@ -1,10 +1,44 @@ -= title_tag t('.title') +- breadcrumb :exports, @workbench -.warning = t('.warning') +.page_content +  .container-fluid +    - if params[:q].present? or collection.any? +      .row +        .col-lg-12 +          = render 'shared/iev_interfaces/filters' -#exports = render 'exports' +    - if collection.any? +      .row +        .col-lg-12 +          = table_builder_2 collection, +            [ \ +              TableBuilderHelper::Column.new( \ +                key: :status, \ +                attribute: Proc.new { |n| export_status(n.status) }, \ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :started_at, \ +                attribute: Proc.new { |n| l(n.started_at, format: :long) if n.started_at }, \ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :name, \ +                attribute: 'name', \ +                link_to: lambda do |export| \ +                  workbench_export_path(@workbench, export) \ +                end \ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :creator, \ +                attribute: 'creator' \ +              ) \ +            ], +            cls: 'table has-search' -- content_for :sidebar do -  ul.actions -    li -      = link_to t('exports.actions.new'), new_referential_export_task_path(@referential), class: 'add'
\ No newline at end of file +          = new_pagination collection, 'pull-right' + +    - unless collection.any? +      .row.mt-xs +        .col-lg-12 +          = replacement_msg t('exports.search_no_results') + += javascript_pack_tag 'date_filters' diff --git a/app/views/exports/index.js.slim b/app/views/exports/index.js.slim deleted file mode 100644 index b9a413b1b..000000000 --- a/app/views/exports/index.js.slim +++ /dev/null @@ -1 +0,0 @@ -| $('#exports').html("#{escape_javascript(render('exports'))}");
\ No newline at end of file diff --git a/app/views/exports/new.html.slim b/app/views/exports/new.html.slim new file mode 100644 index 000000000..f62386caf --- /dev/null +++ b/app/views/exports/new.html.slim @@ -0,0 +1,7 @@ +- breadcrumb :exports, @workbench + +.page_content +  .container-fluid +    .row +      .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 +        = render 'form', export: @export, workbench: @workbench diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 1631e0e7e..2a7d7583c 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -1,26 +1,49 @@ -.title.row -  .col-md-8 -    = title_tag job_status_title(@export) +- breadcrumb :export, @workbench, @export -  .col-md-4 -    = export_attributes_tag(@export) +- page_header_content_for @export -- if @export.report.failure_code? -  .alert.alert-danger -    = t("iev.failure.#{@export.report.failure_code}") +.page_content +  .container-fluid +    .row +      .col-lg-6.col-md-6.col-sm-12.col-xs-12 +        - metadatas = { I18n.t("activerecord.attributes.export.type") => @export.object.class.human_name } +        - metadatas = metadatas.update({I18n.t("activerecord.attributes.export.status") => export_status(@export.status)}) +        - metadatas = metadatas.update({I18n.t("activerecord.attributes.export.parent") => link_to(@export.parent.name, [@export.parent.workbench, @export.parent])}) if @export.parent.present? +        - metadatas = metadatas.update Hash[*@export.visible_options.map{|k, v| [t("activerecord.attributes.export.#{@export.object.class.name.demodulize.underscore}.#{k}"), @export.display_option_value(k, self)]}.flatten] +        - metadatas = metadatas.update({I18n.t("activerecord.attributes.export.file") => (@export.file.present? ? link_to(t("actions.download"), @export.file.url) : "-")}) +        = definition_list t('metadatas'), metadatas -.progress_bars -  = progress_bar_tag(@export) +    .row +      .col-lg-12 +        .error_messages +          = render 'shared/iev_interfaces/messages', messages: @export.messages -.export_show -  .links -    = link_to( font_awesome_classic_tag("fa-file-#{@export.filename_extension}-o") + t("exports.show.exported_file"), exported_file_referential_export_path(@referential, @export.id) ) if @export.file_path -   -  = render partial: "shared/ie_report.html", locals: { job: @export, line_items: @line_items } +    - if @export.children.any? +      .row +        .col-lg-12 +        - coll = @export.children.paginate(page: params[:page] || 1) +        = table_builder_2 coll, +          [ \ +            TableBuilderHelper::Column.new( \ +              key: :status, \ +              attribute: Proc.new { |n| export_status(n.status) }, \ +            ), \ +            TableBuilderHelper::Column.new( \ +              key: :started_at, \ +              attribute: Proc.new { |n| l(n.started_at, format: :long) if n.started_at }, \ +            ), \ +            TableBuilderHelper::Column.new( \ +              key: :name, \ +              attribute: 'name', \ +              link_to: lambda do |export| \ +                workbench_export_path(@workbench, export) \ +              end \ +            ), \ +            TableBuilderHelper::Column.new( \ +              key: :creator, \ +              attribute: 'creator' \ +            ) \ +          ], +          cls: 'table has-search' -- content_for :sidebar do -  ul.actions -    li -      = link_to t('exports.actions.destroy'), referential_export_path(@referential, @export.id), :method => :delete, :data => {:confirm => t('exports.actions.destroy_confirm')}, class: 'remove' -   -  = history_tag(@export)
\ No newline at end of file +        = new_pagination coll, 'pull-right' diff --git a/app/views/imports/_import_messages.html.slim b/app/views/imports/_import_messages.html.slim deleted file mode 100644 index af10b23e5..000000000 --- a/app/views/imports/_import_messages.html.slim +++ /dev/null @@ -1,8 +0,0 @@ -- if import_messages.any? -  ul.list-unstyled.import_message-list -    - import_messages.each do | import_message | -      li -        span(class="#{bootstrap_class_for_message_criticity import_message.criticity}") -          = t( ['import_messages', -            'compliance_check_messages', -            import_message.message_key].join('.'), import_message.message_attributes.symbolize_keys) diff --git a/app/views/imports/index.html.slim b/app/views/imports/index.html.slim index 4fc077bd6..3dff4d80e 100644 --- a/app/views/imports/index.html.slim +++ b/app/views/imports/index.html.slim @@ -2,15 +2,15 @@  .page_content    .container-fluid -    - if params[:q].present? or @imports.any? +    - if params[:q].present? or collection.any?        .row          .col-lg-12 -          = render 'filters' +          = render 'shared/iev_interfaces/filters'      - if @imports.any?        .row          .col-lg-12 -          = table_builder_2 @imports, +          = table_builder_2 collection,              [ \                TableBuilderHelper::Column.new( \                  key: :status, \ @@ -34,9 +34,9 @@              ],              cls: 'table has-search' -          = new_pagination @imports, 'pull-right' +          = new_pagination collection, 'pull-right' -    - unless @imports.any? +    - unless collection.any?        .row.mt-xs          .col-lg-12            = replacement_msg t('imports.search_no_results') diff --git a/app/views/imports/show.html.slim b/app/views/imports/show.html.slim index 7a9d02077..48a4f334c 100644 --- a/app/views/imports/show.html.slim +++ b/app/views/imports/show.html.slim @@ -11,7 +11,7 @@      .row        .col-lg-12          .error_messages -          = render 'import_messages', import_messages: @import.messages +          = render 'shared/iev_interfaces/messages', messages: @import.messages      - if @import.children.any?        .row diff --git a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim index cb0698cf8..02614dcab 100644 --- a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim +++ b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim @@ -29,6 +29,8 @@              span Jeux de données            = link_to workbench_imports_path(current_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do              span Import +          = link_to workbench_exports_path(current_workbench), class: "list-group-item #{(params[:controller] == 'exports') ? 'active' : ''}" do +            span Export            = link_to workgroup_calendars_path(current_workbench.workgroup), class: 'list-group-item' do              span Modèles de calendrier            = link_to workbench_compliance_check_sets_path(current_workbench), class: 'list-group-item' do diff --git a/app/views/layouts/navigation/_page_header.html.slim b/app/views/layouts/navigation/_page_header.html.slim index e407e53da..49f56544b 100644 --- a/app/views/layouts/navigation/_page_header.html.slim +++ b/app/views/layouts/navigation/_page_header.html.slim @@ -1,6 +1,5 @@  - action_links = resource.action_links(params[:action]) rescue nil  - action_links ||= decorated_collection.action_links(params[:action]) rescue nil -  .page_header    .container-fluid      .row @@ -13,7 +12,7 @@              h1 = yield :page_header_title            - else              - if defined?(resource_class) -              h1 = t("#{resource_class.model_name.name.underscore.pluralize}.#{params[:action]}.title") +              h1 = t("#{resource_class.model_name.name.underscore.gsub('/', '.').pluralize}.#{params[:action]}.title")        .col-lg-3.col-md-4.col-sm-5.col-xs-5.text-right          .page-action diff --git a/app/views/imports/_filters.html.slim b/app/views/shared/iev_interfaces/_filters.html.slim index 25c0d10d9..9b114c38d 100644 --- a/app/views/imports/_filters.html.slim +++ b/app/views/shared/iev_interfaces/_filters.html.slim @@ -8,11 +8,11 @@    .ffg-row      .form-group.togglable class=filter_item_class(params[:q], :status_eq_any) -      = f.label Import.human_attribute_name(:status), required: false, class: 'control-label' +      = f.label Import::Base.human_attribute_name(:status), required: false, class: 'control-label'        = f.input :status_eq_any, collection: %w(pending successful warning failed), as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + import_status(l) + "</span>").html_safe}, required: false, wrapper_html: { class: "checkbox_list"}      .form-group.togglable class=filter_item_class(params[:q], :started_at) -      = f.label Import.human_attribute_name(:started_at), required: false, class: 'control-label' +      = f.label Import::Base.human_attribute_name(:started_at), required: false, class: 'control-label'        .filter_menu          = f.simple_fields_for :started_at do |p|            = p.input :start_date, as: :date, label: false, wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @begin_range, include_blank: @begin_range ? false : true diff --git a/app/views/shared/iev_interfaces/_messages.html.slim b/app/views/shared/iev_interfaces/_messages.html.slim new file mode 100644 index 000000000..82f1add57 --- /dev/null +++ b/app/views/shared/iev_interfaces/_messages.html.slim @@ -0,0 +1,14 @@ +- if messages.any? +  ul.list-unstyled.import_message-list +    - messages.order(:created_at).each do | message | +      li +        .row class=bootstrap_class_for_message_criticity(message.criticity) +          - if message.message_attributes["line"] +            .col-md-1= "L. #{message.message_attributes["line"]}" +            .col-md-5= export_message_content message +          - else +            .col-md-6= export_message_content message +          .col-md-6 +            - if message.criticity != "info" +              pre +                = JSON.pretty_generate message.resource_attributes || {} diff --git a/app/workers/simple_export_worker.rb b/app/workers/simple_export_worker.rb new file mode 100644 index 000000000..d41736307 --- /dev/null +++ b/app/workers/simple_export_worker.rb @@ -0,0 +1,10 @@ +class SimpleExportWorker +  include Sidekiq::Worker + +  def perform(export_id) +    export = Export::Base.find(export_id) +    export.update(status: 'running', started_at: Time.now) +    export.call_exporter +    export.update(ended_at: Time.now) +  end +end diff --git a/app/workers/workbench_import_worker.rb b/app/workers/workbench_import_worker.rb index 53cbb222a..fd2a888f0 100644 --- a/app/workers/workbench_import_worker.rb +++ b/app/workers/workbench_import_worker.rb @@ -12,7 +12,7 @@ class WorkbenchImportWorker    def perform(import_id)      @entries = 0 -    @workbench_import ||= WorkbenchImport.find(import_id) +    @workbench_import ||= Import::Workbench.find(import_id)      workbench_import.update(status: 'running', started_at: Time.now)      zip_service = ZipService.new(downloaded, allowed_lines) @@ -44,7 +44,6 @@ class WorkbenchImportWorker      raise    end -    def upload_entry_group entry, element_count      update_object_state entry, element_count.succ      return unless entry.ok? @@ -80,7 +79,6 @@ class WorkbenchImportWorker      File.unlink(eg_file.path)    end -    # Queries    # ======= diff --git a/app/workers/workgroup_export_worker.rb b/app/workers/workgroup_export_worker.rb new file mode 100644 index 000000000..29493cea6 --- /dev/null +++ b/app/workers/workgroup_export_worker.rb @@ -0,0 +1,38 @@ +class WorkgroupExportWorker +  include Sidekiq::Worker + +  attr_reader :workbench_export + +  # Workers +  # ======= + +  def perform(export_id) +    @entries = 0 +    @workbench_export ||= Export::Workgroup.find(export_id) + +    workbench_export.update(status: 'running', started_at: Time.now) +    create_sub_jobs +  rescue Exception => e +    logger.error e.message +    workbench_export.update( status: 'failed' ) +    raise +  end + +  def create_sub_jobs +    # XXX TO DO +    workbench_export.workbench.workgroup.referentials.each do |ref| +      ref.lines.each do |line| +        netex_export = Export::Netex.new +        netex_export.name = "Export line #{line.name} of Referential #{ref.name}" +        netex_export.workbench = workbench_export.workbench +        netex_export.creator = workbench_export.creator +        netex_export.export_type = :line +        netex_export.duration = workbench_export.duration +        netex_export.line_code = line.objectid +        netex_export.parent = workbench_export +        netex_export.save! +      end +    end +  end + +end diff --git a/config/breadcrumbs.rb b/config/breadcrumbs.rb index adcbb0b6f..87d6f5846 100644 --- a/config/breadcrumbs.rb +++ b/config/breadcrumbs.rb @@ -111,11 +111,21 @@ crumb :imports do |workbench|    parent :workbench, workbench  end +crumb :exports do |workbench| +  link I18n.t('exports.index.title'), workbench_exports_path(workbench) +  parent :workbench, workbench +end +  crumb :import do |workbench, import|    link breadcrumb_name(import), workbench_import_path(workbench, import)    parent :imports, workbench  end +crumb :export do |workbench, export| +  link breadcrumb_name(export), workbench_export_path(workbench, export) +  parent :exports, workbench +end +  crumb :import_resources do |import, import_resources|    link I18n.t('import_resources.index.title'), workbench_import_import_resources_path(import.workbench, import.parent)    parent :import, import.workbench, import.parent diff --git a/config/initializers/apartment.rb b/config/initializers/apartment.rb index f5fb8cd5e..6b817caed 100644 --- a/config/initializers/apartment.rb +++ b/config/initializers/apartment.rb @@ -18,72 +18,76 @@ Apartment.configure do |config|    # config.excluded_models = %w{Tenant}    #    config.excluded_models = [ -    'Referential', -    'ReferentialMetadata', -    'ReferentialSuite', -    'Organisation', -    'User',      'Api::V1::ApiKey', -    'StopAreaReferential', -    'StopAreaReferentialMembership', -    'StopAreaReferentialSync', -    'StopAreaReferentialSyncMessage', -    'Chouette::StopArea', -    'LineReferential', -    'LineReferentialMembership', -    'LineReferentialSync', -    'LineReferentialSyncMessage', -    'Chouette::Line', -    'Chouette::GroupOfLine', +    'Calendar',      'Chouette::Company', +    'Chouette::GroupOfLine', +    'Chouette::Line',      'Chouette::Network', -    'ReferentialCloning', -    'Workbench', -    'Workgroup', +    'Chouette::StopArea',      'CleanUp',      'CleanUpResult', -    'Calendar', -    'Import', -    'NetexImport', -    'WorkbenchImport', -    'ImportMessage', -    'ImportResource', +    'ComplianceCheck', +    'ComplianceCheckBlock', +    'ComplianceCheckMessage', +    'ComplianceCheckResource', +    'ComplianceCheckSet',      'ComplianceControl', +    'ComplianceControlBlock', +    'ComplianceControlSet', +    'CustomField', +    'Export::Base', +    'Export::Message', +    'Export::Resource',      'GenericAttributeControl::MinMax',      'GenericAttributeControl::Pattern',      'GenericAttributeControl::Uniqueness', +    'Import::Base', +    'Import::Gtfs', +    'Import::Message', +    'Import::Netex', +    'Import::Resource', +    'Import::Workbench',      'JourneyPatternControl::Duplicates',      'JourneyPatternControl::VehicleJourney',      'LineControl::Route', +    'LineReferential', +    'LineReferentialMembership', +    'LineReferentialSync', +    'LineReferentialSyncMessage', +    'Merge', +    'Organisation', +    'Referential', +    'ReferentialCloning', +    'ReferentialMetadata', +    'ReferentialSuite',      'RouteControl::Duplicates',      'RouteControl::JourneyPattern',      'RouteControl::MinimumLength',      'RouteControl::OmnibusJourneyPattern', -    'RouteControl::OppositeRouteTerminus',      'RouteControl::OppositeRoute', +    'RouteControl::OppositeRouteTerminus',      'RouteControl::StopPointsInJourneyPattern',      'RouteControl::UnactivatedStopPoint',      'RouteControl::ZDLStopArea',      'RoutingConstraintZoneControl::MaximumLength',      'RoutingConstraintZoneControl::MinimumLength',      'RoutingConstraintZoneControl::UnactivatedStopPoint', +    'SimpleExporter', +    'SimpleImporter', +    'SimpleInterface', +    'StopAreaReferential', +    'StopAreaReferentialMembership', +    'StopAreaReferentialSync', +    'StopAreaReferentialSyncMessage', +    'User',      'VehicleJourneyControl::Delta', -    'VehicleJourneyControl::WaitingTime',      'VehicleJourneyControl::Speed',      'VehicleJourneyControl::TimeTable',      'VehicleJourneyControl::VehicleJourneyAtStops', -    'ComplianceControlSet', -    'ComplianceControlBlock', -    'ComplianceCheck', -    'ComplianceCheckSet', -    'ComplianceCheckBlock', -    'ComplianceCheckResource', -    'ComplianceCheckMessage', -    'Merge', -    'CustomField', -    'SimpleInterface', -    'SimpleImporter', -    'SimpleExporter', +    'VehicleJourneyControl::WaitingTime', +    'Workbench', +    'Workgroup',    ]    # use postgres schemas? diff --git a/config/initializers/exporters.rb b/config/initializers/exporters.rb new file mode 100644 index 000000000..dfd82a54c --- /dev/null +++ b/config/initializers/exporters.rb @@ -0,0 +1,6 @@ +SimpleExporter.define :referential_companies do |config| +  config.separator = ";" +  config.encoding = 'ISO-8859-1' +  config.add_column :name +  config.add_column :registration_number +end diff --git a/config/locales/export_messages.en.yml b/config/locales/export_messages.en.yml new file mode 100644 index 000000000..f7951a103 --- /dev/null +++ b/config/locales/export_messages.en.yml @@ -0,0 +1,3 @@ +en: +  export_messages: +    success: Success diff --git a/config/locales/export_messages.fr.yml b/config/locales/export_messages.fr.yml new file mode 100644 index 000000000..5c2191f35 --- /dev/null +++ b/config/locales/export_messages.fr.yml @@ -0,0 +1,3 @@ +fr: +  export_messages: +    success: Succès diff --git a/config/locales/exports.en.yml b/config/locales/exports.en.yml index 2a47fba54..88c1b99f8 100644 --- a/config/locales/exports.en.yml +++ b/config/locales/exports.en.yml @@ -1,25 +1,32 @@  en: -  exports: +  exports: &exports +    search_no_results: "No export matching your query" +    filters: +      referential: "Select data space..." +      name_or_creator_cont: "Select an export or creator name..." +      error_period_filter: "End date must be greater or equal than begin date"      actions:        new: "New export" +      create: "New export" +      show: "Export report" +      download: "Download original file"        destroy: "Destroy"        destroy_confirm: "Are you sure you want destroy this export?" -    new: -      title: "New export" -      all: "All" -      flash: "Export task on queue, refresh page to see progression"      index:        title: "Exports"        warning: "" +    new: +      title: "Generate a new export" +    create: +      title: "Generate a new export"      show: +      title: "Export %{name}"        report: "Report" -      exported_file: "Exported file" -    statuses: -      started: "Started"       -      scheduled: "Processing ..." -      terminated: "Completed" -      canceled: "Canceled" -      aborted: "Failed"     +      exported_file: "Original file" +      compliance_check: "Validation report" +      compliance_check_of: "Validation of export: " +      export_of_validation: "Export of the validation" +    compliance_check_task: "Validate Report"      severities:        info: "Information"        uncheck: "Unchecked" @@ -27,7 +34,13 @@ en:        warning: "Warning"        error: "Error"        fatal: "Fatal" -  activemodel: +  export: +    workgroup: Workgroup +    netex:     Netex +    referential_companies: Companies +    base: +      <<: *exports +  activerecord:      models:        export:          zero:  "export" @@ -37,6 +50,10 @@ en:          zero:  "export"          one:   "Neptune export"          other: "exports" +      csv_export: +        zero:  "export" +        one:   "CSV export" +        other: "exports"        gtfs_export:          zero:  "export"          one:   "GTFS export" @@ -44,4 +61,39 @@ en:        netex_export:          zero:  "export"          one:   "NeTEx export" -        other: "exports"
\ No newline at end of file +        other: "exports" +    errors: +      models: +        export: +          base: +            attributes: +              file: +                wrong_file_extension: "The exported file must be a zip file" +    attributes: +      attrs: &attrs +        resources: "File to export" +        created_at: "Created on" +        started_at: "Started at" +        name: "Name" +        status: "Status" +        creator: "Creator" +        references_type: "Data to be exported" +        no_save: "No save" +        object_id_prefix: "Neptune Id prefix" +        max_distance_for_commercial: "Max distance for commercial stop" +        max_distance_for_connection_link: "Max distance for connection link" +        ignore_last_word: "ignore last word" +        ignore_end_chars: "ignore last chars" +        parent: Parent +      export: +        <<: *attrs +        base: +          <<: *attrs +        workgroup: +          duration: Duration +        referential_companies: +          referential_id: Referential +  flash: +    exports: +      create: +        notice: "The export is in progress. Please wait and refresh the page in a few moments." diff --git a/config/locales/exports.fr.yml b/config/locales/exports.fr.yml index 2d7cc0259..fa3ac8fc7 100644 --- a/config/locales/exports.fr.yml +++ b/config/locales/exports.fr.yml @@ -1,33 +1,46 @@  fr: -  exports: +  exports: &exports +    search_no_results: "Aucun export ne correspond à votre recherche" +    filters: +      referential: "Sélectionnez un jeu de données..." +      name_or_creator_cont: "Indiquez un nom d'export ou d'opérateur..." +      error_period_filter: "La date de fin doit être supérieure ou égale à la date de début"      actions:        new: "Nouvel export" +      create: "Nouvel export" +      show: "Rapport d'export" +      download: "Téléch. fichier source"        destroy: "Supprimer cet export"        destroy_confirm: "Etes vous sûr de supprimer cet export ?" -    new: -      title: "Nouvel export" -      all: "Toutes" -      flash: "La demande d'export est mise en file d'attente, veuillez rafraichir régulièrement la page pour en suivre la progression"      index:        title: "Exports"        warning: "" +    new: +      title: "Générer un export" +    create: +      title: "Générer un export"      show: +      title: "Export %{name}"        report: "Rapport" -      exported_file: "Fichier exporté" -    statuses: -      started: "En file d'attente..." -      scheduled: "En cours..." -      terminated: "Achevé" -      canceled: "Annulé" -      aborted: "Echoué" +      exported_file: "Fichier source" +      compliance_check: "Test de conformité" +      compliance_check_of: "Validation de l'export : " +      export_of_validation: "L'export de la validation" +    compliance_check_task: "Validation"      severities:        info: "Information" -      uncheck: "Non disponible" +      uncheck: "Non testé"        ok: "Ok"        warning: "Alerte"        error: "Erreur"        fatal: "Fatal" -  activemodel: +  export: +    workgroup: Groupe de travail +    netex:     Netex +    referential_companies: Transporteurs +    base: +      <<: *exports +  activerecord:      models:        export:          zero:  "export" @@ -35,7 +48,11 @@ fr:          other: "exports"        neptune_export:          zero:  "export" -        one:   "export neptune" +        one:   "export Neptune" +        other: "exports" +      csv_export: +        zero:  "export" +        one:   "export CSV"          other: "exports"        gtfs_export:          zero:  "export" @@ -45,3 +62,40 @@ fr:          zero:  "export"          one:   "export NeTEx"          other: "exports" +    errors: +      models: +        export: +          base: +            attributes: +              file: +                wrong_file_extension: "Le fichier exporté doit être au format zip" +    attributes: +      attrs: &attrs +        resources: "Fichier à exporter" +        created_at: "Créé le" +        started_at: Démarrage +        name: "Nom de l'export" +        status: "Etat" +        creator: "Opérateur" +        no_save: "Pas de sauvegarde" +        references_type: "Données à exporter" +        object_id_prefix: "Préfixe d'identifiants" +        max_distance_for_commercial: "Distance max pour créer les zones" +        max_distance_for_connection_link: "Distance max pour créer les correspondances" +        ignore_last_word: "ignorer le dernier mot" +        ignore_end_chars: "ignorer les n derniers caractères" +        type: "Type d'export" +        file: "Résultat" +        parent: Parent +      export: +        <<: *attrs +        base: +          <<: *attrs +        workgroup: +          duration: Durée +        referential_companies: +          referential_id: Jeu de données +  flash: +    exports: +      create: +        notice: "L'export est en cours, veuillez patienter. Actualiser votre page si vous voulez voir l'avancement de votre traitement." diff --git a/config/locales/import_messages.en.yml b/config/locales/import_messages.en.yml index aad4fb772..bc06c46f0 100644 --- a/config/locales/import_messages.en.yml +++ b/config/locales/import_messages.en.yml @@ -1,5 +1,5 @@  en: -  import_messages: +  import_message:      corrupt_zip_file: "The zip file %{source_filename} is corrupted and cannot be read"      inconsistent_zip_file: "The zip file %{source_filename} contains unexpected directories: %{spurious_dirs}, which are ignored"      referential_creation: "Le référentiel n'a pas pu être créé car un référentiel existe déjà sur les mêmes périodes et lignes" @@ -50,4 +50,4 @@ en:      2_netexstif_servicejourneypattern_2: "%{source_filename}-Ligne %{source_line_number}-Colonne %{source_column_number} : l'objet ServiceJourneyPattern d'identifiant %{source_objectid} doit contenir au moins 2 StopPointInJourneyPattern"      2_netexstif_servicejourneypattern_3_1: "%{source_filename}-Ligne %{source_line_number}-Colonne %{source_column_number} : l'objet ServiceJourneyPattern d'identifiant %{source_objectid} n'a pas de valeur pour l'attribut ServiceJourneyPatternType"      2_netexstif_servicejourneypattern_3_2: "%{source_filename}-Ligne %{source_line_number}-Colonne %{source_column_number} : l'objet ServiceJourneyPattern d'identifiant %{source_objectid} a une valeur interdite %{error_value} pour l'attribut ServiceJourneyPatternType différente de 'passenger'" -    2_netexstif_servicejourneypattern_4: "%{source_filename}-Ligne %{source_line_number}-Colonne %{source_column_number}, objet ServiceJourneyPattern d'identifiant %{source_objectid} : les attributs 'order' des StopPointInJourneyPattern ne sont pas croissants."
\ No newline at end of file +    2_netexstif_servicejourneypattern_4: "%{source_filename}-Ligne %{source_line_number}-Colonne %{source_column_number}, objet ServiceJourneyPattern d'identifiant %{source_objectid} : les attributs 'order' des StopPointInJourneyPattern ne sont pas croissants." diff --git a/config/locales/import_resources.en.yml b/config/locales/import_resources.en.yml index 5f0f3213e..3e737f9bc 100644 --- a/config/locales/import_resources.en.yml +++ b/config/locales/import_resources.en.yml @@ -1,5 +1,5 @@  en: -  import_resources: +  import_resources: &resources      index:        title: "NeTEx conformity"        table_state: "%{lines_imported} line(s) imported on %{lines_in_zipfile} presents in zipfile" diff --git a/config/locales/imports.en.yml b/config/locales/imports.en.yml index b0644acd3..d0db87fb1 100644 --- a/config/locales/imports.en.yml +++ b/config/locales/imports.en.yml @@ -59,11 +59,12 @@ en:      errors:        models:          import: -          attributes: -            file: -              wrong_file_extension: "The imported file must be a zip file" +          base: +            attributes: +              file: +                wrong_file_extension: "The imported file must be a zip file"      attributes: -      import: +      attrs: &attrs          resources: "File to import"          created_at: "Created on"          started_at: "Started at" @@ -77,6 +78,10 @@ en:          max_distance_for_connection_link: "Max distance for connection link"          ignore_last_word: "ignore last word"          ignore_end_chars: "ignore last chars" +      import: +        <<: *attrs +        base: +          <<: *attrs    flash:      imports:        create: diff --git a/config/locales/imports.fr.yml b/config/locales/imports.fr.yml index 2380eac45..40272889a 100644 --- a/config/locales/imports.fr.yml +++ b/config/locales/imports.fr.yml @@ -1,5 +1,5 @@  fr: -  imports: +  imports: &imports      search_no_results: "Aucun import ne correspond à votre recherche"      filters:        referential: "Sélectionnez un jeu de données..." @@ -34,6 +34,9 @@ fr:        warning: "Alerte"        error: "Erreur"        fatal: "Fatal" +  import: +    base: +      <<: *imports    activerecord:      models:        import: @@ -59,11 +62,12 @@ fr:      errors:        models:          import: -          attributes: -            file: -              wrong_file_extension: "Le fichier importé doit être au format zip" +          base: +            attributes: +              file: +                wrong_file_extension: "Le fichier importé doit être au format zip"      attributes: -      import: +      attrs: &attrs          resources: "Fichier à importer"          created_at: "Créé le"          started_at: Démarrage @@ -77,6 +81,12 @@ fr:          max_distance_for_connection_link: "Distance max pour créer les correspondances"          ignore_last_word: "ignorer le dernier mot"          ignore_end_chars: "ignorer les n derniers caractères" + +      import: +        <<: *attrs +        base: +          <<: *attrs +    flash:      imports:        create: diff --git a/config/routes.rb b/config/routes.rb index b6934936b..6313b5678 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,6 +11,12 @@ ChouetteIhm::Application.routes.draw do          resources :import_messages, only: [:index]        end      end +    resources :exports do +      post :upload, on: :member +      resources :export_resources, only: [:index] do +        resources :export_messages, only: [:index] +      end +    end      resources :compliance_check_sets, only: [:index, :show] do        get :executed, on: :member        resources :compliance_checks, only: [:show] diff --git a/db/migrate/20171106111448_update_import_message_criticity_type_to_string.rb b/db/migrate/20171106111448_update_import_message_criticity_type_to_string.rb index c14450387..a08a10b9a 100644 --- a/db/migrate/20171106111448_update_import_message_criticity_type_to_string.rb +++ b/db/migrate/20171106111448_update_import_message_criticity_type_to_string.rb @@ -7,10 +7,10 @@ class UpdateImportMessageCriticityTypeToString < ActiveRecord::Migration        when 0 then "info"        when 1 then "warning"        when 2 then "error" -      else  +      else          "info"        end      end -    ImportMessage.all.each { |im| im.update_attribute(:criticity, change_criticity_value(im.criticity)) } +    Import::Message.all.each { |im| im.update_attribute(:criticity, change_criticity_value(im.criticity)) }    end  end diff --git a/db/migrate/20180306135204_clean_former_exports.rb b/db/migrate/20180306135204_clean_former_exports.rb new file mode 100644 index 000000000..46a595c12 --- /dev/null +++ b/db/migrate/20180306135204_clean_former_exports.rb @@ -0,0 +1,5 @@ +class CleanFormerExports < ActiveRecord::Migration +  def change +    drop_table :exports +  end +end diff --git a/db/migrate/20180306152953_update_imports_names.rb b/db/migrate/20180306152953_update_imports_names.rb new file mode 100644 index 000000000..42129a580 --- /dev/null +++ b/db/migrate/20180306152953_update_imports_names.rb @@ -0,0 +1,13 @@ +class UpdateImportsNames < ActiveRecord::Migration +  def change +    Import::Base.all.pluck(:type).uniq.each do |type| +      next if type =~ /^Import/ +      Import::Base.where(type: type).update_all type: "Import::#{type.gsub 'Import', ''}" +    end + +    Import::Base.all.pluck(:parent_type).uniq.each do |type| +      next if type =~ /^Import/ +      Import::Base.where(parent_type: type).update_all parent_type: "Import::#{type.gsub 'Import', ''}" +    end +  end +end diff --git a/db/migrate/20180307071448_create_new_exports.rb b/db/migrate/20180307071448_create_new_exports.rb new file mode 100644 index 000000000..74921d108 --- /dev/null +++ b/db/migrate/20180307071448_create_new_exports.rb @@ -0,0 +1,56 @@ +class CreateNewExports < ActiveRecord::Migration +  def change +    create_table :exports do |t| +      t.string   "status" +      t.string   "current_step_id" +      t.float    "current_step_progress" +      t.integer  "workbench_id",          limit: 8 +      t.integer  "referential_id",        limit: 8 +      t.string   "name" +      t.datetime "created_at" +      t.datetime "updated_at" +      t.string   "file" +      t.datetime "started_at" +      t.datetime "ended_at" +      t.string   "token_upload" +      t.string   "type" +      t.integer  "parent_id",             limit: 8 +      t.string   "parent_type" +      t.datetime "notified_parent_at" +      t.integer  "current_step",          default: 0 +      t.integer  "total_steps",           default: 0 +      t.string   "creator" +    end + +    add_index "exports", ["referential_id"], name: "index_exports_on_referential_id", using: :btree +    add_index "exports", ["workbench_id"], name: "index_exports_on_workbench_id", using: :btree + +    create_table "export_messages", id: :bigserial, force: :cascade do |t| +      t.string   "criticity" +      t.string   "message_key" +      t.hstore   "message_attributes" +      t.integer  "export_id",           limit: 8 +      t.integer  "resource_id",         limit: 8 +      t.datetime "created_at" +      t.datetime "updated_at" +      t.hstore   "resource_attributes" +    end + +    add_index "export_messages", ["export_id"], name: "index_export_messages_on_export_id", using: :btree +    add_index "export_messages", ["resource_id"], name: "index_export_messages_on_resource_id", using: :btree + +    create_table "export_resources", id: :bigserial, force: :cascade do |t| +      t.integer  "export_id",     limit: 8 +      t.string   "status" +      t.datetime "created_at" +      t.datetime "updated_at" +      t.string   "resource_type" +      t.string   "reference" +      t.string   "name" +      t.hstore   "metrics" +    end + +    add_index "export_resources", ["export_id"], name: "index_export_resources_on_export_id", using: :btree + +  end +end diff --git a/db/migrate/20180308095116_add_options_to_exports.rb b/db/migrate/20180308095116_add_options_to_exports.rb new file mode 100644 index 000000000..02744c5cb --- /dev/null +++ b/db/migrate/20180308095116_add_options_to_exports.rb @@ -0,0 +1,5 @@ +class AddOptionsToExports < ActiveRecord::Migration +  def change +    add_column :exports, :options, :hstore +  end +end diff --git a/db/schema.rb b/db/schema.rb index e29d076e0..885f12e24 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@  #  # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180301142531) do +ActiveRecord::Schema.define(version: 20180308095116) do    # These are extensions that must be enabled in order to support this database    enable_extension "plpgsql" @@ -299,18 +299,58 @@ ActiveRecord::Schema.define(version: 20180301142531) do    add_index "custom_fields", ["resource_type"], name: "index_custom_fields_on_resource_type", using: :btree +  create_table "export_messages", id: :bigserial, force: :cascade do |t| +    t.string   "criticity" +    t.string   "message_key" +    t.hstore   "message_attributes" +    t.integer  "export_id",           limit: 8 +    t.integer  "resource_id",         limit: 8 +    t.datetime "created_at" +    t.datetime "updated_at" +    t.hstore   "resource_attributes" +  end + +  add_index "export_messages", ["export_id"], name: "index_export_messages_on_export_id", using: :btree +  add_index "export_messages", ["resource_id"], name: "index_export_messages_on_resource_id", using: :btree + +  create_table "export_resources", id: :bigserial, force: :cascade do |t| +    t.integer  "export_id",     limit: 8 +    t.string   "status" +    t.datetime "created_at" +    t.datetime "updated_at" +    t.string   "resource_type" +    t.string   "reference" +    t.string   "name" +    t.hstore   "metrics" +  end + +  add_index "export_resources", ["export_id"], name: "index_export_resources_on_export_id", using: :btree +    create_table "exports", id: :bigserial, force: :cascade do |t| -    t.integer  "referential_id",  limit: 8      t.string   "status" -    t.string   "type" -    t.string   "options" +    t.string   "current_step_id" +    t.float    "current_step_progress" +    t.integer  "workbench_id",          limit: 8 +    t.integer  "referential_id",        limit: 8 +    t.string   "name"      t.datetime "created_at"      t.datetime "updated_at" -    t.string   "references_type" -    t.string   "reference_ids" +    t.string   "file" +    t.datetime "started_at" +    t.datetime "ended_at" +    t.string   "token_upload" +    t.string   "type" +    t.integer  "parent_id",             limit: 8 +    t.string   "parent_type" +    t.datetime "notified_parent_at" +    t.integer  "current_step",                    default: 0 +    t.integer  "total_steps",                     default: 0 +    t.string   "creator" +    t.hstore   "options"    end    add_index "exports", ["referential_id"], name: "index_exports_on_referential_id", using: :btree +  add_index "exports", ["workbench_id"], name: "index_exports_on_workbench_id", using: :btree    create_table "facilities", id: :bigserial, force: :cascade do |t|      t.integer  "stop_area_id",       limit: 8 diff --git a/lib/stif/permission_translator.rb b/lib/stif/permission_translator.rb index 9e0feb9b8..09a7c610c 100644 --- a/lib/stif/permission_translator.rb +++ b/lib/stif/permission_translator.rb @@ -21,6 +21,7 @@ module Stif          calendars          footnotes          imports +        exports          merges          journey_patterns          referentials diff --git a/spec/controllers/api/v1/imports_controller_spec.rb b/spec/controllers/api/v1/imports_controller_spec.rb index 8077dd052..f7022115a 100644 --- a/spec/controllers/api/v1/imports_controller_spec.rb +++ b/spec/controllers/api/v1/imports_controller_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Api::V1::ImportsController, type: :controller do        it 'should be successful' do          expect {            post :create, workbench_id: workbench.id, workbench_import: {name: "test", file: file, creator: 'test'}, format: :json -        }.to change{WorkbenchImport.count}.by(1) +        }.to change{Import::Workbench.count}.by(1)          expect(response).to be_success        end      end diff --git a/spec/controllers/exports_controller_spec.rb b/spec/controllers/exports_controller_spec.rb index 6cd6e4c54..3a67497ec 100644 --- a/spec/controllers/exports_controller_spec.rb +++ b/spec/controllers/exports_controller_spec.rb @@ -1,22 +1,97 @@ -require 'spec_helper' - -describe ExportsController, :type => :controller do +RSpec.describe ExportsController, :type => :controller do    login_user -  describe "GET 'new'" do -    it "returns http success" do -      pending -      get 'new' +  let(:workbench) { create :workbench } +  let(:export)    { create(:netex_export, workbench: workbench) } + +  describe 'GET #new' do +    it 'should be successful if authorized' do +      get :new, workbench_id: workbench.id        expect(response).to be_success      end + +    it 'should be unsuccessful unless authorized' do +      remove_permissions('exports.create', from_user: @user, save: true) +      get :new, workbench_id: workbench.id +      expect(response).not_to be_success +    end    end -  describe "GET 'index'" do -    it "returns http success" do -      pending -      get 'index' -      expect(response).to be_success +  describe "POST #create" do +    let(:params){ {name: "foo"} } +    let(:request){ post :create, workbench_id: workbench.id, export: params  } +    it 'should create no objects' do +      expect{request}.to_not change{Export::Base.count} +    end + +    context "with full params" do +      let(:params){{ +        name: "foo", +        type: "Export::Netex", +        duration: 12, +        export_type: :line +      }} + +      it 'should be successful' do +        expect{request}.to change{Export::Base.count}.by(1) +      end + +      it "displays a flash message" do +        request +        expect(controller).to set_flash[:notice].to( +          I18n.t('flash.exports.create.notice') +        ) +      end +    end + +    context "with missing options" do +      let(:params){{ +        name: "foo", +        type: "Export::Workgroup" +      }} + +      it 'should be unsuccessful' do +        expect{request}.to change{Export::Base.count}.by(0) +      end +    end + +    context "with all options" do +      let(:params){{ +        name: "foo", +        type: "Export::Workgroup", +        duration: 90 +      }} + +      it 'should be successful' do +        expect{request}.to change{Export::Base.count}.by(1) +      end +    end + +    context "with wrong type" do +      let(:params){{ +        name: "foo", +        type: "Export::Foo" +      }} + +      it 'should be unsuccessful' do +        expect{request}.to raise_error ActiveRecord::SubclassNotFound +      end      end    end +  describe 'POST #upload' do +    context "with the token" do +      it 'should be successful' do +        post :upload, workbench_id: workbench.id, id: export.id, token: export.token_upload +        expect(response).to be_redirect +      end +    end + +    context "without the token" do +      it 'should be unsuccessful' do +        post :upload, workbench_id: workbench.id, id: export.id, token: "foo" +        expect(response).to_not be_success +      end +    end +  end  end diff --git a/spec/controllers/vehicle_journey_imports_controller_spec.rb b/spec/controllers/vehicle_journey_imports_controller_spec.rb deleted file mode 100644 index 633f90b70..000000000 --- a/spec/controllers/vehicle_journey_imports_controller_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'spec_helper' - -describe ImportTasksController, :type => :controller do -  login_user -end diff --git a/spec/factories/exports.rb b/spec/factories/exports.rb deleted file mode 100644 index 34427edb8..000000000 --- a/spec/factories/exports.rb +++ /dev/null @@ -1,5 +0,0 @@ -FactoryGirl.define do -  factory :export do -    referential { Referential.find_by_slug("first") } -  end -end diff --git a/spec/factories/exports/export_messages.rb b/spec/factories/exports/export_messages.rb new file mode 100644 index 000000000..55394ec45 --- /dev/null +++ b/spec/factories/exports/export_messages.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do +  factory :export_message, class: Export::Message do +    association :export +    association :resource, factory: :export_resource +    criticity :info  +  end +end diff --git a/spec/factories/exports/export_resources.rb b/spec/factories/exports/export_resources.rb new file mode 100644 index 000000000..8e38235cd --- /dev/null +++ b/spec/factories/exports/export_resources.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do +  factory :export_resource, class: Export::Resource do +    sequence(:name) { |n| "Export resource #{n}" } +    association :export, factory: :netex_export +    status :WARNING +    resource_type 'type' +    reference 'reference' +  end +end diff --git a/spec/factories/exports/exports.rb b/spec/factories/exports/exports.rb new file mode 100644 index 000000000..c8aaf30a9 --- /dev/null +++ b/spec/factories/exports/exports.rb @@ -0,0 +1,34 @@ +FactoryGirl.define do +  factory :export, class: Export::Base do +    sequence(:name) { |n| "Export #{n}" } +    current_step_id "MyString" +    current_step_progress 1.5 +    association :workbench +    association :referential +    status :new +    started_at nil +    ended_at nil +    creator 'rspec' + +    after(:build) do |export| +      export.class.skip_callback(:create, :before, :initialize_fields) +    end +  end + +  factory :bad_export, class: Export::Base do +    sequence(:name) { |n| "Export #{n}" } +    current_step_id "MyString" +    current_step_progress 1.5 +    association :workbench +    association :referential +    file {File.open(File.join(Rails.root, 'spec', 'fixtures', 'terminated_job.json'))} +    status :new +    started_at nil +    ended_at nil +    creator 'rspec' + +    after(:build) do |export| +      export.class.skip_callback(:create, :before, :initialize_fields) +    end +  end +end diff --git a/spec/factories/exports/netex_exports.rb b/spec/factories/exports/netex_exports.rb new file mode 100644 index 000000000..0648bbc56 --- /dev/null +++ b/spec/factories/exports/netex_exports.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do +  factory :netex_export, class: Export::Netex, parent: :export do +    association :parent, factory: :workgroup_export +    export_type :line +    duration 90 +  end +end diff --git a/spec/factories/exports/workgroup_exports.rb b/spec/factories/exports/workgroup_exports.rb new file mode 100644 index 000000000..f5dfb6b94 --- /dev/null +++ b/spec/factories/exports/workgroup_exports.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do +  factory :workgroup_export, class: Export::Workgroup, parent: :export do +    duration 90 +  end +end diff --git a/spec/factories/import_messages.rb b/spec/factories/imports/import_messages.rb index 5d936679a..f5edf1685 100644 --- a/spec/factories/import_messages.rb +++ b/spec/factories/imports/import_messages.rb @@ -1,5 +1,5 @@  FactoryGirl.define do -  factory :import_message do +  factory :import_message, class: Import::Message do      association :import      association :resource, factory: :import_resource      criticity :info diff --git a/spec/factories/import_resources.rb b/spec/factories/imports/import_resources.rb index 76afcc486..aaf7e3111 100644 --- a/spec/factories/import_resources.rb +++ b/spec/factories/imports/import_resources.rb @@ -1,5 +1,5 @@  FactoryGirl.define do -  factory :import_resource do +  factory :import_resource, class: Import::Resource do      association :import      status :WARNING      sequence(:name) { |n| "Import resource #{n}" } diff --git a/spec/factories/imports.rb b/spec/factories/imports/imports.rb index e07447b60..cb7764cc6 100644 --- a/spec/factories/imports.rb +++ b/spec/factories/imports/imports.rb @@ -1,5 +1,5 @@  FactoryGirl.define do -  factory :import do +  factory :import, class: Import::Base do      sequence(:name) { |n| "Import #{n}" }      current_step_id "MyString"      current_step_progress 1.5 @@ -16,7 +16,7 @@ FactoryGirl.define do      end    end -  factory :bad_import do +  factory :bad_import, class: Import::Base do      sequence(:name) { |n| "Import #{n}" }      current_step_id "MyString"      current_step_progress 1.5 diff --git a/spec/factories/netex_imports.rb b/spec/factories/imports/netex_imports.rb index b59267a0a..7ee6839e8 100644 --- a/spec/factories/netex_imports.rb +++ b/spec/factories/imports/netex_imports.rb @@ -1,7 +1,7 @@  FactoryGirl.define do -  factory :netex_import, class: NetexImport, parent: :import do +  factory :netex_import, class: Import::Netex, parent: :import do      file { File.open(Rails.root.join('spec', 'fixtures', 'OFFRE_TRANSDEV_2017030112251.zip')) }      association :parent, factory: :workbench_import -     +    end  end diff --git a/spec/factories/workbench_imports.rb b/spec/factories/imports/workbench_imports.rb index 466bfe688..5ed1ee4e5 100644 --- a/spec/factories/workbench_imports.rb +++ b/spec/factories/imports/workbench_imports.rb @@ -1,5 +1,5 @@  FactoryGirl.define do -  factory :workbench_import, class: WorkbenchImport, parent: :import do +  factory :workbench_import, class: Import::Workbench, parent: :import do      file { File.open(Rails.root.join('spec', 'fixtures', 'OFFRE_TRANSDEV_2017030112251.zip')) }    end  end diff --git a/spec/models/export/export_message_spec.rb b/spec/models/export/export_message_spec.rb new file mode 100644 index 000000000..61a3b6319 --- /dev/null +++ b/spec/models/export/export_message_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe Export::Message, :type => :model do +  it { should validate_presence_of(:criticity) } +  it { should belong_to(:export) } +  it { should belong_to(:resource) } +end diff --git a/spec/models/export/export_resource_spec.rb b/spec/models/export/export_resource_spec.rb new file mode 100644 index 000000000..7537cd2a8 --- /dev/null +++ b/spec/models/export/export_resource_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +RSpec.describe Export::Resource, :type => :model do +  it { should belong_to(:export) } + +  it { should enumerize(:status).in("OK", "ERROR", "WARNING", "IGNORED") } + +  it { should validate_presence_of(:name) } +  it { should validate_presence_of(:resource_type) } +  it { should validate_presence_of(:reference) } + +  describe 'states' do +    let(:export_resource) { create(:export_resource) } + +    it 'should initialize with new state' do +      expect(export_resource.status).to eq("WARNING") +    end +  end +end diff --git a/spec/models/export/export_spec.rb b/spec/models/export/export_spec.rb new file mode 100644 index 000000000..ca94c1ff1 --- /dev/null +++ b/spec/models/export/export_spec.rb @@ -0,0 +1,239 @@ +RSpec.describe Export::Base, type: :model do + +  it { should belong_to(:referential) } +  it { should belong_to(:workbench) } +  it { should belong_to(:parent) } + +  it { should enumerize(:status).in("aborted", "canceled", "failed", "new", "pending", "running", "successful", "warning") } + +  it { should validate_presence_of(:workbench) } +  it { should validate_presence_of(:creator) } + +  include ActionDispatch::TestProcess +  it { should allow_value(fixture_file_upload('OFFRE_TRANSDEV_2017030112251.zip')).for(:file) } +  it { should_not allow_value(fixture_file_upload('reflex_updated.xml')).for(:file).with_message(I18n.t('errors.messages.extension_whitelist_error', extension: '"xml"', allowed_types: "zip, csv, json")) } + +  let(:workgroup_export) {netex_export.parent} +  let(:workgroup_export_with_completed_steps) do +    build_stubbed( +      :workgroup_export, +      total_steps: 2, +      current_step: 2 +    ) +  end + +  let(:netex_export) do +    create( +      :netex_export +    ) +  end + +  describe ".abort_old" do +    it "changes exports older than 4 hours to aborted" do +      Timecop.freeze(Time.now) do +        old_export = create( +          :workgroup_export, +          status: 'pending', +          created_at: 4.hours.ago - 1.minute +        ) +        current_export = create(:workgroup_export, status: 'pending') + +        Export::Base.abort_old + +        expect(current_export.reload.status).to eq('pending') +        expect(old_export.reload.status).to eq('aborted') +      end +    end + +    it "doesn't work on exports with a `finished_status`" do +      Timecop.freeze(Time.now) do +        export = create( +          :workgroup_export, +          status: 'successful', +          created_at: 4.hours.ago - 1.minute +        ) + +        Export::Base.abort_old + +        expect(export.reload.status).to eq('successful') +      end +    end + +    it "only works on the caller type" do +      Timecop.freeze(Time.now) do +        workgroup_export = create( +          :workgroup_export, +          status: 'pending', +          created_at: 4.hours.ago - 1.minute +        ) +        netex_export = create( +          :netex_export, +          status: 'pending', +          created_at: 4.hours.ago - 1.minute +        ) + +        Export::Netex.abort_old + +        expect(workgroup_export.reload.status).to eq('pending') +        expect(netex_export.reload.status).to eq('aborted') +      end +    end +  end + +  describe "#destroy" do +    it "must destroy all child exports" do +      netex_export = create(:netex_export) + +      netex_export.parent.destroy + +      expect(netex_export.parent).to be_destroyed +      expect(Export::Netex.count).to eq(0) +    end + +    it "must destroy all associated Export::Messages" do +      export = create(:netex_export) +      create(:export_resource, export: export) + +      export.destroy + +      expect(Export::Resource.count).to eq(0) +    end + +    it "must destroy all associated Export::Resources" do +      export = create(:netex_export) +      create(:export_message, export: export) + +      export.destroy + +      expect(Export::Message.count).to eq(0) +    end +  end + +  describe "#notify_parent" do +    it "must call #child_change on its parent" do +      allow(netex_export).to receive(:update) + +      expect(workgroup_export).to receive(:child_change) +      netex_export.status = :foo +      netex_export.notify_parent +    end + +    it "must update the :notified_parent_at field of the child export" do +      allow(workgroup_export).to receive(:child_change) + +      Timecop.freeze(Time.now) do +        netex_export.status = :bar + +        netex_export.notify_parent +        expect(netex_export.notified_parent_at).to eq Time.now +        expect(netex_export.reload.notified_parent_at).to eq Time.now +      end +    end +  end + +  describe "#child_change" do +    it "calls #update_status" do +      allow(workgroup_export).to receive(:update) + +      expect(workgroup_export).to receive(:update_status) +      workgroup_export.child_change +    end +  end + +  describe "#update_status" do +    shared_examples( +      "updates :status to failed when >=1 child has failing status" +    ) do |failure_status| +      it "updates :status to failed when >=1 child has failing status" do +        workgroup_export = create(:workgroup_export) +        create( +          :netex_export, +          parent: workgroup_export, +          status: failure_status +        ) + +        workgroup_export.update_status + +        expect(workgroup_export.status).to eq('failed') +      end +    end + +    include_examples( +      "updates :status to failed when >=1 child has failing status", +      "failed" +    ) +    include_examples( +      "updates :status to failed when >=1 child has failing status", +      "aborted" +    ) +    include_examples( +      "updates :status to failed when >=1 child has failing status", +      "canceled" +    ) + +    it "updates :status to successful when all children are successful" do +      workgroup_export = create(:workgroup_export) +      exports = create_list( +        :netex_export, +        2, +        parent: workgroup_export, +        status: 'successful' +      ) + +      workgroup_export.update_status + +      expect(workgroup_export.status).to eq('successful') +    end + +    it "updates :status to failed when any child has failed" do +      workgroup_export = create(:workgroup_export) +      [ +        'failed', +        'successful' +      ].each do |status| +        create( +          :netex_export, +          parent: workgroup_export, +          status: status +        ) +      end + +      workgroup_export.update_status + +      expect(workgroup_export.status).to eq('failed') +    end + +    it "updates :status to warning when any child has warning or successful" do +      workgroup_export = create(:workgroup_export) +      [ +        'warning', +        'successful' +      ].each do |status| +        create( +          :netex_export, +          parent: workgroup_export, +          status: status +        ) +      end + +      workgroup_export.update_status + +      expect(workgroup_export.status).to eq('warning') +    end + +    it "updates :ended_at to now when status is finished" do +      workgroup_export = create(:workgroup_export) +      create( +        :netex_export, +        parent: workgroup_export, +        status: 'failed' +      ) + +      Timecop.freeze(Time.now) do +        workgroup_export.update_status + +        expect(workgroup_export.ended_at).to eq(Time.now) +      end +    end +  end +end diff --git a/spec/models/export/netex_export_spec.rb b/spec/models/export/netex_export_spec.rb new file mode 100644 index 000000000..d9cccd6ad --- /dev/null +++ b/spec/models/export/netex_export_spec.rb @@ -0,0 +1,19 @@ +RSpec.describe Export::Netex, type: [:model, :with_commit] do + +  let( :boiv_iev_uri ){  URI("#{Rails.configuration.iev_url}/boiv_iev/referentials/exporter/new?id=#{subject.id}")} + +  before do +    allow(Thread).to receive(:new).and_yield +  end + +  context 'with referential' do +    subject{ build( :netex_export, id: random_int ) } + +    it 'will trigger the Java API' do +      with_stubbed_request(:get, boiv_iev_uri) do |request| +        with_commit{ subject.save! } +        expect(request).to have_been_requested +      end +    end +  end +end diff --git a/spec/models/export/workgroup_export_spec.rb b/spec/models/export/workgroup_export_spec.rb new file mode 100644 index 000000000..c812b2b21 --- /dev/null +++ b/spec/models/export/workgroup_export_spec.rb @@ -0,0 +1,10 @@ +RSpec.describe Export::Workgroup, type: [:model, :with_commit] do +  it { should validate_presence_of(:duration) } + +  it "should set options" do +    expect(Export::Workgroup.options).to have_key :duration +    expect(Export::Workgroup.options[:duration][:required]).to be_truthy +    expect(Export::Workgroup.options[:duration][:default_value]).to eq 90 +    expect(Export::Workgroup.options[:duration][:type]).to eq :integer +  end +end diff --git a/spec/models/export_log_message_spec.rb b/spec/models/export_log_message_spec.rb deleted file mode 100644 index 5ab32dec0..000000000 --- a/spec/models/export_log_message_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe ExportLogMessage, :type => :model do - -  # describe "#attributes" do - -  #   subject { create :export_log_message } - -  #   it "should read json stored in database" do -  #     subject.update_attribute :arguments, { "key" => "value"} -  #     expect(subject.raw_attributes).to eq({ "key" => "value"}.to_json) -  #   end - -  # end - -end diff --git a/spec/models/export_spec.rb b/spec/models/export_spec.rb deleted file mode 100644 index 13953078a..000000000 --- a/spec/models/export_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# require 'spec_helper' - -# describe Export, :type => :model do - -#   subject { create :export } - -#   RSpec::Matchers.define :be_log_message do |expected| -#     match do |actual| -#       actual and expected.all? { |k,v| actual[k.to_s] == v } -#     end -#   end - -#   describe "#export" do - -#     before(:each) do -#       allow(subject).to receive_messages :exporter => double(:export => true) -#     end - -#     it "should create a ExportLogmessage :started when started" do -#       subject.export -#       expect(subject.log_messages.first).to be_log_message(:key => "started") -#     end - -#     it "should create a ExportLogmessage :completed when completed" do -#       subject.export -#       expect(subject.log_messages.last).to be_log_message(:key => "completed") -#     end - -#     it "should create a ExportLogmessage :failed when failed" do -#       pending -#       # subject.loader.stub(:export).and_raise("export failed") -#       subject.export -#       expect(subject.log_messages.last).to be_log_message(:key => "failed") -#     end - -#   end - -#   describe "#options" do - -#     it "should be empty by default" do -#       expect(subject.options).to be_empty -#     end - -#   end - -#   describe ".types" do - -#     it "should return available Export implementations" do -#       expect(Export.types).to match_array(%w{NeptuneExport CsvExport GtfsExport NetexExport KmlExport HubExport}) -#     end - -#   end - -#   describe ".new" do - -#     it "should use type attribute to create a subclass" do -#       expect(Export.new(:type => "NeptuneExport")).to be_an_instance_of(NeptuneExport) -#     end - -#   end - -#   it_behaves_like TypeIdsModelable do -#     let(:type_ids_model) { subject} -#   end - -# end diff --git a/spec/models/export_task_spec.rb b/spec/models/export_task_spec.rb deleted file mode 100644 index 1a52a6175..000000000 --- a/spec/models/export_task_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'spec_helper' - -describe ExportTask, :type => :model do - -  it { should_not validate_presence_of(:start_date) } -  it { should_not validate_presence_of(:end_date) } - -end diff --git a/spec/models/gtfs_export_spec.rb b/spec/models/gtfs_export_spec.rb index ccc98e872..0ef3660f5 100644 --- a/spec/models/gtfs_export_spec.rb +++ b/spec/models/gtfs_export_spec.rb @@ -1,33 +1,33 @@  require 'spec_helper' -describe GtfsExport, :type => :model do - -  describe "#time_zone" do - -    context "when exported data are not StopAreas" do - -      before do -        subject.references_type = "network" -      end - -      it "should be mandatory" do -        should validate_presence_of(:time_zone) -      end - -    end - -    context "when export data are StopArea" do - -      before do -        subject.references_type = "stop_area" -      end - -      it "should be mandatory" do -        should_not validate_presence_of(:time_zone) -      end - -    end - -  end - -end +# describe GtfsExport, :type => :model do +# +#   describe "#time_zone" do +# +#     context "when exported data are not StopAreas" do +# +#       before do +#         subject.references_type = "network" +#       end +# +#       it "should be mandatory" do +#         should validate_presence_of(:time_zone) +#       end +# +#     end +# +#     context "when export data are StopArea" do +# +#       before do +#         subject.references_type = "stop_area" +#       end +# +#       it "should be mandatory" do +#         should_not validate_presence_of(:time_zone) +#       end +# +#     end +# +#   end +# +# end diff --git a/spec/models/gtfs_import_spec.rb b/spec/models/gtfs_import_spec.rb index 07cc1905d..5cb69332c 100644 --- a/spec/models/gtfs_import_spec.rb +++ b/spec/models/gtfs_import_spec.rb @@ -1,6 +1,6 @@  require 'spec_helper' -describe GtfsImport, :type => :model do +describe Import::Gtfs, :type => :model do   # describe "#object_id_prefix" do diff --git a/spec/models/import_message_spec.rb b/spec/models/import/import_message_spec.rb index 2d8aac2b7..48e03a2cc 100644 --- a/spec/models/import_message_spec.rb +++ b/spec/models/import/import_message_spec.rb @@ -1,6 +1,6 @@  require 'rails_helper' -RSpec.describe ImportMessage, :type => :model do +RSpec.describe Import::Message, :type => :model do    it { should validate_presence_of(:criticity) }    it { should belong_to(:import) }    it { should belong_to(:resource) } diff --git a/spec/models/import_resource_spec.rb b/spec/models/import/import_resource_spec.rb index c88bb5dd2..7d2eab8f1 100644 --- a/spec/models/import_resource_spec.rb +++ b/spec/models/import/import_resource_spec.rb @@ -1,6 +1,6 @@  require 'rails_helper' -RSpec.describe ImportResource, :type => :model do +RSpec.describe Import::Resource, :type => :model do    it { should belong_to(:import) }    it { should enumerize(:status).in("OK", "ERROR", "WARNING", "IGNORED") } diff --git a/spec/models/import_spec.rb b/spec/models/import/import_spec.rb index 8b85f151b..c41d5ba53 100644 --- a/spec/models/import_spec.rb +++ b/spec/models/import/import_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Import, type: :model do +RSpec.describe Import::Base, type: :model do    it { should belong_to(:referential) }    it { should belong_to(:workbench) } @@ -24,7 +24,7 @@ RSpec.describe Import, type: :model do    end    let(:netex_import) do -    build_stubbed( +    create(        :netex_import      )    end @@ -39,7 +39,7 @@ RSpec.describe Import, type: :model do          )          current_import = create(:workbench_import, status: 'pending') -        Import.abort_old +        Import::Base.abort_old          expect(current_import.reload.status).to eq('pending')          expect(old_import.reload.status).to eq('aborted') @@ -54,7 +54,7 @@ RSpec.describe Import, type: :model do            created_at: 4.hours.ago - 1.minute          ) -        Import.abort_old +        Import::Base.abort_old          expect(import.reload.status).to eq('successful')        end @@ -73,7 +73,7 @@ RSpec.describe Import, type: :model do            created_at: 4.hours.ago - 1.minute          ) -        NetexImport.abort_old +        Import::Netex.abort_old          expect(workbench_import.reload.status).to eq('pending')          expect(netex_import.reload.status).to eq('aborted') @@ -88,25 +88,25 @@ RSpec.describe Import, type: :model do        netex_import.parent.destroy        expect(netex_import.parent).to be_destroyed -      expect(NetexImport.count).to eq(0) +      expect(Import::Netex.count).to eq(0)      end -    it "must destroy all associated ImportMessages" do +    it "must destroy all associated Import::Messages" do        import = create(:import)        create(:import_resource, import: import)        import.destroy -      expect(ImportResource.count).to eq(0) +      expect(Import::Resource.count).to eq(0)      end -    it "must destroy all associated ImportResources" do +    it "must destroy all associated Import::Resources" do        import = create(:import)        create(:import_message, import: import)        import.destroy -      expect(ImportMessage.count).to eq(0) +      expect(Import::Message.count).to eq(0)      end    end @@ -115,19 +115,18 @@ RSpec.describe Import, type: :model do        allow(netex_import).to receive(:update)        expect(workbench_import).to receive(:child_change) - +      netex_import.status = :foo        netex_import.notify_parent      end      it "must update the :notified_parent_at field of the child import" do        allow(workbench_import).to receive(:child_change) - -      Timecop.freeze(DateTime.now) do -        expect(netex_import).to receive(:update).with( -          notified_parent_at: DateTime.now -        ) +      Timecop.freeze(Time.now) do +        netex_import.status = :bar          netex_import.notify_parent +        expect(netex_import.notified_parent_at).to eq Time.now +        expect(netex_import.reload.notified_parent_at).to eq Time.now        end      end    end diff --git a/spec/models/import/netex_import_spec.rb b/spec/models/import/netex_import_spec.rb index 8ffeed1f4..6424fbfe1 100644 --- a/spec/models/import/netex_import_spec.rb +++ b/spec/models/import/netex_import_spec.rb @@ -1,8 +1,7 @@ -RSpec.describe NetexImport, type: [:model, :with_commit] do +RSpec.describe Import::Netex, type: [:model, :with_commit] do    let( :boiv_iev_uri ){  URI("#{Rails.configuration.iev_url}/boiv_iev/referentials/importer/new?id=#{subject.id}")} -    before do      allow(Thread).to receive(:new).and_yield    end @@ -30,4 +29,42 @@ RSpec.describe NetexImport, type: [:model, :with_commit] do      end    end +  describe "#destroy" do +    it "must destroy its associated Referential if ready: false" do +      workbench_import = create(:workbench_import) +      referential_ready_false = create(:referential, ready: false) +      referential_ready_true = create(:referential, ready: true) +      create( +        :netex_import, +        parent: workbench_import, +        referential: referential_ready_false +      ) +      create( +        :netex_import, +        parent: workbench_import, +        referential: referential_ready_true +      ) + +      workbench_import.destroy + +      expect( +        Referential.where(id: referential_ready_false.id).exists? +      ).to be false +      expect( +        Referential.where(id: referential_ready_true.id).exists? +      ).to be true +    end + +    it "doesn't try to destroy nil referentials" do +      workbench_import = create(:workbench_import) +      create( +        :netex_import, +        parent: workbench_import, +        referential: nil +      ) + +      expect { workbench_import.destroy }.not_to raise_error +    end +  end +  end diff --git a/spec/models/import_service_spec.rb b/spec/models/import_service_spec.rb deleted file mode 100644 index e7ee062d6..000000000 --- a/spec/models/import_service_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe ImportService, :type => :model do - -  let(:referential) { create(:referential, :slug => "test") } -   -  subject { ImportService.new(referential) } - -  describe '.find' do -     -    it "should build an import with a scheduled job" do       -    end - -    it "should build an import with a terminated job" do -    end -     -  end -   -end diff --git a/spec/models/import_task_spec.rb b/spec/models/import_task_spec.rb deleted file mode 100644 index 3aa006a69..000000000 --- a/spec/models/import_task_spec.rb +++ /dev/null @@ -1,196 +0,0 @@ -# require 'spec_helper' - -# describe ImportTask, :type => :model do - -#   subject { build :import_task } - -#   describe ".new" do - -#     it "should use type attribute to create a subclass" do -#       expect(ImportTask.new(:format => "Neptune")).to be_an_instance_of(NeptuneImport) -#       expect(ImportTask.new(:format => "Gtfs")).to be_an_instance_of(GtfsImport) -#       expect(ImportTask.new(:format => "Netex")).to be_an_instance_of(NetexImport) -#       expect(ImportTask.new(:format => "Csv")).to be_an_instance_of(CsvImport) - -#       expect(NeptuneImport.new).to be_an_instance_of(NeptuneImport) -#       expect(GtfsImport.new).to be_an_instance_of(GtfsImport) -#       expect(NetexImport.new).to be_an_instance_of(NetexImport) -#       expect(CsvImport.new).to be_an_instance_of(CsvImport) -#     end - -#   end - -#   describe "#delayed_import" do -#     before(:each) do -#       allow(subject).to receive_messages( :delay => double( :import => true)) -#     end -#     it "should call delay#import" do -#       expect(subject.delay).to receive( :import) -#       subject.send :delayed_import -#     end -#   end - -#   describe ".create" do -#     before(:each) do -#       allow(subject).to receive_messages( :save_resources => true ) -#     end -#     it "should call save_resource" do -#       expect(subject).to receive( :save_resources) -#       subject.send :save -#     end -#     it "should update file_path with #saved_resources" do -#       subject.send :save -#       expect(ImportTask.find( subject.id).file_path).to eq(subject.send( :saved_resources)) -#     end -#     it "should have a compliance_check_task" do -#       subject.send :save -#       expect(ImportTask.find( subject.id).compliance_check_task).not_to be_nil -#     end -#   end - -#   describe "#compliance_check_task" do -#     let(:rule_parameter_set){ Factory( :rule_parameter_set) } -#     let(:import_task){ Factory(:import_task, :rule_parameter_set_id => rule_parameter_set.id) } -#     let(:compliance_check_task){ import_task.compliance_check_task } - -#     it "should have same #referential as import_task" do -#       expect(compliance_check_task.referential).to eq(import_task.referential) -#     end - -#     it "should have same #rule_parameter_set_id as import_task" do -#       expect(compliance_check_task.rule_parameter_set_id).to eq(import_task.rule_parameter_set_id) -#     end - -#     it "should have same #user_id as import_task" do -#       expect(compliance_check_task.user_id).to eq(import_task.user_id) -#     end - -#     it "should have same #user_name as import_task" do -#       expect(compliance_check_task.user_name).to eq(import_task.user_name) -#     end -#   end - -#   describe "#file_path_extension" do -#     let(:import_task){ Factory(:import_task) } -#     context "zip file to import" do -#       before(:each) do -#         import_task.file_path = "aaa/bbb.zip" -#       end -#       it "should return zip" do -#         expect(import_task.file_path_extension).to eq("zip") -#       end -#     end -#     context "xml file to import" do -#       before(:each) do -#         import_task.file_path = "aaa/bbb.xml" -#       end -#       it "should return xml" do -#         expect(import_task.file_path_extension).to eq("xml") -#       end -#     end -#     context "csv file to import" do -#       before(:each) do -#         import_task.file_path = "aaa/bbb.csv" -#       end -#       it "should return csv" do -#         expect(import_task.file_path_extension).to eq("basic") -#       end -#     end - -#   end - -#   context "options attributes" do -#     let(:import_task){ Factory(:import_task) } -#     describe "#no_save" do -#       it "should read parameter_set['no_save']" do -#         import_task.parameter_set[ "no_save"] = "dummy" -#         expect(import_task.no_save).to eq("dummy") -#       end -#     end -#     describe "#format" do -#       it "should read parameter_set['format']" do -#         import_task.parameter_set[ "format"] = "dummy" -#         expect(import_task.format).to eq("dummy") -#       end -#     end -#     describe "#file_path" do -#       it "should read parameter_set['file_path']" do -#         import_task.parameter_set[ "file_path"] = "dummy" -#         expect(import_task.file_path).to eq("dummy") -#       end -#     end -#     describe "#no_save=" do -#       it "should read parameter_set['no_save']" do -#         import_task.no_save = "dummy" -#         expect(import_task.parameter_set[ "no_save"]).to eq(false) -#       end -#     end -#     describe "#format=" do -#       it "should read parameter_set['format']" do -#         import_task.format = "dummy" -#         expect(import_task.parameter_set[ "format"]).to eq("dummy") -#       end -#     end -#     describe "#file_path=" do -#       it "should read parameter_set['file_path']" do -#         import_task.file_path = "dummy" -#         expect(import_task.parameter_set[ "file_path"]).to eq("dummy") -#       end -#     end -#   end - -#   describe "#chouette_command" do -#     it "should be a Chouette::Command instance" do -#       expect(subject.send( :chouette_command).class).to eq(Chouette::Command) -#     end -#     it "should have schema same as referential.slug" do -#       expect(subject.send( :chouette_command).schema).to eq(subject.referential.slug) -#     end -#   end - -#   describe "#import" do -#     let(:import_task){ Factory(:import_task) } -#     let(:chouette_command) { "dummy" } -#     context "for failing import" do -#       before(:each) do -#         allow(chouette_command).to receive( :run!).and_raise( "dummy") -#         allow(import_task).to receive_messages( :chouette_command => chouette_command) -#       end -#       it "should have status 'failed'" do -#         import_task.import -#         expect(import_task.status).to eq("failed") -#       end -#       it "should have status 'failed' for compliance_check_task" do -#         import_task.import -#         expect(import_task.compliance_check_task.status).to eq("failed") -#       end -#     end -#     context "for successful import" do -#       before(:each) do -#         allow(import_task).to receive_messages( :chouette_command => double( :run! => true )) -#       end -#       it "should have status 'completed'" do -#         import_task.import -#         expect(import_task.status).to eq("completed") -#       end -#       it "should have status 'completed' for compliance_check_task" do -#         import_task.import -#         expect(import_task.status).to eq("completed") -#       end -#     end -#   end - -#   describe "#import" do -#     let(:import_task){ Factory(:import_task) } -#     let(:command_args){ "dummy" } -#     before(:each) do -#       allow(import_task).to receive_messages( :chouette_command => double( :run! => true )) -#       allow(import_task).to receive_messages( :chouette_command_args => command_args) -#     end -#     it "should call chouette_command.run! with :c => 'import', :id => id" do -#       expect(import_task.send( :chouette_command)).to receive( :run! ).with(  command_args) -#       import_task.import -#     end -#   end - -# end diff --git a/spec/models/netex_export_spec.rb b/spec/models/netex_export_spec.rb index 1d09fa07f..345bf4d5a 100644 --- a/spec/models/netex_export_spec.rb +++ b/spec/models/netex_export_spec.rb @@ -1,10 +1,10 @@  require 'spec_helper' -describe NetexExport, :type => :model do - -  # describe '#export_options' do -  #   subject { super().export_options } -  #   it { is_expected.to include(:format => :netex) } -  # end -   -end +# describe NetexExport, :type => :model do +# +#   # describe '#export_options' do +#   #   subject { super().export_options } +#   #   it { is_expected.to include(:format => :netex) } +#   # end +# +# end diff --git a/spec/models/netex_import_spec.rb b/spec/models/netex_import_spec.rb deleted file mode 100644 index c6051a869..000000000 --- a/spec/models/netex_import_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -RSpec.describe NetexImport, type: :model do -  describe "#destroy" do -    it "must destroy its associated Referential if ready: false" do -      workbench_import = create(:workbench_import) -      referential_ready_false = create(:referential, ready: false) -      referential_ready_true = create(:referential, ready: true) -      create( -        :netex_import, -        parent: workbench_import, -        referential: referential_ready_false -      ) -      create( -        :netex_import, -        parent: workbench_import, -        referential: referential_ready_true -      ) - -      workbench_import.destroy - -      expect( -        Referential.where(id: referential_ready_false.id).exists? -      ).to be false -      expect( -        Referential.where(id: referential_ready_true.id).exists? -      ).to be true -    end - -    it "doesn't try to destroy nil referentials" do -      workbench_import = create(:workbench_import) -      create( -        :netex_import, -        parent: workbench_import, -        referential: nil -      ) - -      expect { workbench_import.destroy }.not_to raise_error -    end -  end -end diff --git a/spec/models/simple_exporter_spec.rb b/spec/models/simple_exporter_spec.rb index 75051aeb9..a42daafe1 100644 --- a/spec/models/simple_exporter_spec.rb +++ b/spec/models/simple_exporter_spec.rb @@ -5,7 +5,7 @@ RSpec.describe SimpleExporter do          SimpleExporter.define :foo          expect do            SimpleExporter.new(configuration_name: :test).export -        end.to raise_error +        end.to raise_error(RuntimeError)        end      end      context "with a complete configuration" do @@ -18,9 +18,9 @@ RSpec.describe SimpleExporter do        it "should define an exporter" do          expect{SimpleExporter.find_configuration(:foo)}.to_not raise_error          expect{SimpleExporter.new(configuration_name: :foo, filepath: "").export}.to_not raise_error -        expect{SimpleExporter.find_configuration(:bar)}.to raise_error -        expect{SimpleExporter.new(configuration_name: :bar, filepath: "")}.to raise_error -        expect{SimpleExporter.new(configuration_name: :bar, filepath: "").export}.to raise_error +        expect{SimpleExporter.find_configuration(:bar)}.to raise_error(RuntimeError) +        expect{SimpleExporter.new(configuration_name: :bar, filepath: "")}.to raise_error(RuntimeError) +        expect{SimpleExporter.new(configuration_name: :bar, filepath: "").export}.to raise_error(RuntimeError)          expect{SimpleExporter.create(configuration_name: :foo, filepath: "")}.to change{SimpleExporter.count}.by 1        end      end @@ -33,7 +33,7 @@ RSpec.describe SimpleExporter do              config.add_column :name              config.add_column :name            end -        end.to raise_error +        end.to raise_error(RuntimeError)        end      end    end diff --git a/spec/models/simple_importer_spec.rb b/spec/models/simple_importer_spec.rb index 5f9eb0651..8f4d7cfdd 100644 --- a/spec/models/simple_importer_spec.rb +++ b/spec/models/simple_importer_spec.rb @@ -6,7 +6,7 @@ RSpec.describe SimpleImporter do          SimpleImporter.define :foo          expect do            SimpleImporter.new(configuration_name: :foo, filepath: "").import -        end.to raise_error +        end.to raise_error(RuntimeError)        end      end      context "with a complete configuration" do @@ -20,8 +20,8 @@ RSpec.describe SimpleImporter do          expect{SimpleImporter.find_configuration(:foo)}.to_not raise_error          expect{SimpleImporter.new(configuration_name: :foo, filepath: "")}.to_not raise_error          expect{SimpleImporter.new(configuration_name: :foo, filepath: "").import}.to_not raise_error -        expect{SimpleImporter.find_configuration(:bar)}.to raise_error -        expect{SimpleImporter.new(configuration_name: :bar, filepath: "")}.to raise_error +        expect{SimpleImporter.find_configuration(:bar)}.to raise_error(RuntimeError) +        expect{SimpleImporter.new(configuration_name: :bar, filepath: "")}.to raise_error(RuntimeError)          expect{SimpleImporter.create(configuration_name: :foo, filepath: "")}.to change{SimpleImporter.count}.by 1        end      end @@ -49,7 +49,7 @@ RSpec.describe SimpleImporter do      end      it "should import the given file" do -      expect{importer.import verbose: true}.to change{Chouette::StopArea.count}.by 1 +      expect{importer.import verbose: false}.to change{Chouette::StopArea.count}.by 1        expect(importer.status).to eq "success"        stop = Chouette::StopArea.last        expect(stop.name).to eq "Nom du Stop" diff --git a/spec/requests/api/v1/netex_import_spec.rb b/spec/requests/api/v1/netex_import_spec.rb index 8597c1d32..14dac9a25 100644 --- a/spec/requests/api/v1/netex_import_spec.rb +++ b/spec/requests/api/v1/netex_import_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe "NetexImport", type: :request do +RSpec.describe "Import::Netex", type: :request do    describe 'POST netex_imports' do @@ -39,7 +39,7 @@ RSpec.describe "NetexImport", type: :request do          post_request.(netex_import: legal_attributes)          expect( response ).to be_success          expect( json_response_body ).to eq( -          'id'             => NetexImport.last.id, +          'id'             => Import::Netex.last.id,            'referential_id' => Referential.last.id,            'workbench_id'   => workbench.id          ) @@ -51,7 +51,7 @@ RSpec.describe "NetexImport", type: :request do          create(:line, objectid: 'STIF:CODIFLIGNE:Line:C00108', line_referential: workbench.line_referential)          create(:line, objectid: 'STIF:CODIFLIGNE:Line:C00109', line_referential: workbench.line_referential) -        expect{ post_request.(netex_import: legal_attributes) }.to change{NetexImport.count}.by(1) +        expect{ post_request.(netex_import: legal_attributes) }.to change{Import::Netex.count}.by(1)        end        it 'creates a correct Referential', pending: 'see #5073' do @@ -96,7 +96,7 @@ RSpec.describe "NetexImport", type: :request do            end            it 'does not create an Import object' do -            expect{ post_request.(netex_import: illegal_attributes) }.not_to change{Import.count} +            expect{ post_request.(netex_import: illegal_attributes) }.not_to change{Import::Base.count}            end            it 'might not create a referential' do diff --git a/spec/services/parent_notifier_spec.rb b/spec/services/parent_notifier_spec.rb index ecf508fcd..d2dc6b184 100644 --- a/spec/services/parent_notifier_spec.rb +++ b/spec/services/parent_notifier_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ParentNotifier do          expect(netex_import).to receive(:notify_parent)        end -      ParentNotifier.new(Import).notify_when_finished(netex_imports) +      ParentNotifier.new(Import::Base).notify_when_finished(netex_imports)      end      it "doesn't call #notify_parent if its `notified_parent_at` is set" do @@ -33,7 +33,7 @@ RSpec.describe ParentNotifier do        expect(netex_import).not_to receive(:notify_parent) -      ParentNotifier.new(Import).notify_when_finished +      ParentNotifier.new(Import::Base).notify_when_finished      end    end @@ -46,8 +46,10 @@ RSpec.describe ParentNotifier do          notified_parent_at: nil        ) +      Import::Base.where(id: netex_import).update_all notified_parent_at: nil +        expect( -        ParentNotifier.new(Import).objects_pending_notification +        ParentNotifier.new(Import::Base).objects_pending_notification        ).to eq([netex_import])      end @@ -55,7 +57,7 @@ RSpec.describe ParentNotifier do        create(:import, parent: nil)        expect( -        ParentNotifier.new(Import).objects_pending_notification +        ParentNotifier.new(Import::Base).objects_pending_notification        ).to be_empty      end @@ -70,7 +72,7 @@ RSpec.describe ParentNotifier do        end        expect( -        ParentNotifier.new(Import).objects_pending_notification +        ParentNotifier.new(Import::Base).objects_pending_notification        ).to be_empty      end @@ -83,7 +85,7 @@ RSpec.describe ParentNotifier do        )        expect( -        ParentNotifier.new(Import).objects_pending_notification +        ParentNotifier.new(Import::Base).objects_pending_notification        ).to be_empty      end    end diff --git a/spec/support/permissions.rb b/spec/support/permissions.rb index 95afd6c1c..825e44725 100644 --- a/spec/support/permissions.rb +++ b/spec/support/permissions.rb @@ -17,6 +17,7 @@ module Support          connection_links          calendars          footnotes +        exports          imports          merges          journey_patterns | 
