diff options
| -rw-r--r-- | app/controllers/exports_controller.rb | 85 | ||||
| -rw-r--r-- | app/controllers/imports_controller.rb | 2 | ||||
| -rw-r--r-- | app/helpers/exports_helper.rb | 36 | ||||
| -rw-r--r-- | app/helpers/imports_helper.rb | 23 | ||||
| -rw-r--r-- | app/models/export.rb | 102 | ||||
| -rw-r--r-- | app/models/export_report.rb | 113 | ||||
| -rw-r--r-- | app/models/export_service.rb | 23 | ||||
| -rw-r--r-- | app/models/export_task.rb | 171 | ||||
| -rw-r--r-- | app/models/export_type.rb | 7 | ||||
| -rw-r--r-- | db/migrate/20150319082515_change_collation_for_tag_names.acts_as_taggable_on_engine.rb | 10 |
10 files changed, 505 insertions, 67 deletions
diff --git a/app/controllers/exports_controller.rb b/app/controllers/exports_controller.rb index 941d293ae..0fb66294d 100644 --- a/app/controllers/exports_controller.rb +++ b/app/controllers/exports_controller.rb @@ -1,20 +1,91 @@ require 'will_paginate/array' class ExportsController < ChouetteController - respond_to :html, :xml, :json + defaults :resource_class => Export + + respond_to :xml, :json + respond_to :html, :only => [:show, :index, :new, :create, :delete] respond_to :js, :only => [:show, :index] belongs_to :referential - protected + #curl -F "file=@corolis.zip;filename=corolis_gtfs.zip" -F "file=@parameters.json;filename=parameters.json" http://localhost:8080/chouette_iev/referentials/corolis/exporter/gtfs + + def index + begin + index! do + build_breadcrumb :index + end + rescue Ievkit::Error => error + logger.error("Iev failure : #{error.message}") + flash[:error] = t('iev.failure') + redirect_to referential_path(@referential) + end + end - def test - test = IevApi.jobs(@referential.slug, { :action => "exporter" }).map do |export_hash| - export = Export.new(export_hash) + def show + begin + show! do + build_breadcrumb :show + end + rescue Ievkit::Error => error + logger.error("Iev failure : #{error.message}") + flash[:error] = t('iev.failure') + redirect_to referential_path(@referential) end end - def collection - @exports ||= test.paginate(:page => params[:page]) + def new + begin + new! do + puts "OK" + end + rescue Ievkit::Error => error + logger.error("Iev failure : #{error.message}") + flash[:error] = t('iev.failure') + redirect_to referential_path(@referential) + end + end + + def create + begin + create! do + puts "OK" + end + rescue Ievkit::Error => error + logger.error("Iev failure : #{error.message}") + flash[:error] = t('iev.failure') + redirect_to referential_path(@referential) + end end + def delete + begin + delete! do + export_service.delete(@export.id) + redirect_to referential_exports_path(@referential) + end + rescue Ievkit::Error => error + logger.error("Iev failure : #{error.message}") + flash[:error] = t('iev.failure') + redirect_to referential_path(@referential) + end + end + + protected + + def export_service + ExportService.new(@referential) + end + + def build_resource(attributes = {}) + @export ||= ExportTask.new + end + + def resource + @export ||= export_service.find( params[:id] ) + end + + def collection + @exports ||= export_service.all.paginate(:page => params[:page]) + end end diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 927c188e2..e7ff7ff15 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -7,7 +7,7 @@ class ImportsController < ChouetteController respond_to :js, :only => [:show, :index] belongs_to :referential - # create => curl -F "file=@Citura_050115_220215_ref.zip;filename=Citura_050115_220215_ref.zip" -F "file=@parameters.json;filename=parameters.json" http://localhost:8080/mobi.chouette.api/referentials/test/importer/neptune + # create => curl -F "file=@Citura.zip;filename=Citura.zip" -F "file=@parameters.json;filename=parameters.json" http://localhost:8080/chouette_iev/referentials/test/importer/neptune # index curl http://localhost:8080/mobi.chouette.api/referentials/test/jobs # show curl http://localhost:8080/mobi.chouette.api/referentials/test/jobs diff --git a/app/helpers/exports_helper.rb b/app/helpers/exports_helper.rb index 9ee5778d6..cb1f42f36 100644 --- a/app/helpers/exports_helper.rb +++ b/app/helpers/exports_helper.rb @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- module ExportsHelper - - def fields_for_export_format(form) + + def fields_for_export_task_format(form) begin render :partial => export_partial_name(form), :locals => { :form => form } rescue ActionView::MissingTemplate @@ -10,29 +10,27 @@ module ExportsHelper end def export_partial_name(form) - "fields_#{form.object.format.underscore}" + "fields_#{form.object.format.underscore}_export" + end + + def compliance_icon( export_task) + return nil unless export_task.compliance_check_task + export_task.compliance_check_task.tap do |cct| + if cct.failed? || cct.any_error_severity_failure? + return 'icons/link_page_alert.png' + else + return 'icons/link_page.png' + end + end end def export_progress_bar_tag(export) - - if export.canceled? || export.aborted? - div_class = "progress-bar progress-bar-danger" - elsif export.scheduled? - div_class = "progress-bar progress-bar-info" - elsif export.created? - div_class = "progress-bar progress-bar-info" - elsif export.terminated? - div_class = "progress-bar progress-bar-success" - else - div_class = "" - end - + div_class = "" content_tag :div, :class => "progress" do content_tag :div, :class => div_class, role: "progressbar", :'aria-valuenow' => "#{export.percentage_progress}", :'aria-valuemin' => "0", :'aria-valuemax' => "100", :style => "width: #{export.percentage_progress}%;" do - "#{export.percentage_progress}% " + I18n.t("exports.statuses.#{export.export_status}") + "#{export.percentage_progress}% " + I18n.t("export_tasks.statuses.#{export.status}") end end - end - + end diff --git a/app/helpers/imports_helper.rb b/app/helpers/imports_helper.rb index 498d03bf9..2df2de93f 100644 --- a/app/helpers/imports_helper.rb +++ b/app/helpers/imports_helper.rb @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- module ImportsHelper - + def fields_for_import_task_format(form) - #partial_name = "fields_#{form.object.format.underscore}_import" - begin render :partial => import_partial_name(form), :locals => { :form => form } rescue ActionView::MissingTemplate @@ -14,7 +12,7 @@ module ImportsHelper def import_partial_name(form) "fields_#{form.object.format.underscore}_import" end - + def compliance_icon( import_task) return nil unless import_task.compliance_check_task import_task.compliance_check_task.tap do |cct| @@ -25,27 +23,14 @@ module ImportsHelper end end end - + def import_progress_bar_tag(import) - - # if import.canceled? || import.aborted? - # div_class = "progress-bar progress-bar-danger" - # elsif import.scheduled? - # div_class = "progress-bar progress-bar-info" - # elsif import.created? - # div_class = "progress-bar progress-bar-info" - # elsif import.terminated? - # div_class = "progress-bar progress-bar-success" - # else - div_class = "" - # end - + div_class = "" content_tag :div, :class => "progress" do content_tag :div, :class => div_class, role: "progressbar", :'aria-valuenow' => "#{import.percentage_progress}", :'aria-valuemin' => "0", :'aria-valuemax' => "100", :style => "width: #{import.percentage_progress}%;" do "#{import.percentage_progress}% " + I18n.t("import_tasks.statuses.#{import.status}") end end - end end diff --git a/app/models/export.rb b/app/models/export.rb index 7ee9704f6..6b60f3df1 100644 --- a/app/models/export.rb +++ b/app/models/export.rb @@ -3,53 +3,113 @@ class Export extend ActiveModel::Naming include ActiveModel::Model - enumerize :export_status, in: %w{created scheduled terminated canceled aborted}, default: "created", predicates: true - enumerize :export_format, in: %w{neptune netex gtfs hub kml}, default: "neptune", predicates: true - attr_reader :datas - def initialize(options=Hashie::Mash.new) - @datas = options - @export_status = @datas.status.downcase if @datas.status - @export_format = @datas.type.downcase if @datas.type + def initialize(response) + @datas = response + end + + def report + report_path = datas.links[:report] + if report_path + response = IevApi.request(:get, compliance_check_path, params) + ExportReport.new(response) + else + raise IevApi::IevError("Impossible to access report path link for import") + end + end + + def compliance_check + compliance_check_path = datas.links[:validation] + if compliance_check_path + response = IevApi.request(:get, compliance_check_path, params) + ComplianceCheck.new(response) + else + raise IevApi::IevError("Impossible to access compliance check path link for import") + end + end + + def delete + delete_path = datas.links[:delete] + if delete_path + IevApi.request(:delete, delete_path, params) + else + raise IevApi::IevError("Impossible to access delete path link for import") + end + end + + def cancel + cancel_path = datas.links[:cancel] + if cancel_path + IevApi.request(:delete, cancel_path, params) + else + raise IevApi::IevError("Impossible to access cancel path link for import") + end + end + + def id + datas.id + end + + def status + datas.status + end + + def format + datas.format end + def filename + datas.filename + end + + def filename_extension + File.extname(filename) if filename + end + def percentage_progress - if %w{created}.include? export_status + if %w{created}.include? status 0 - elsif %w{ terminated canceled aborted }.include? export_status + elsif %w{ terminated canceled aborted }.include? status 100 else 20 end end - - def links - @datas.links + + def referential_name + datas.referential end - + def name - @datas.parameters.name + datas.action_parameters.name end - + def user_name - @datas.parameters.user_name + datas.action_parameters.user_name end def no_save - @datas.parameters.no_save + datas.action_parameters.no_save end def filename - @datas.filename + datas.filename end + def created_at? + datas.created? + end + def created_at - Time.at(@datas.created.to_i / 1000) + Time.at(datas.created.to_i / 1000) if created_at? end + def updated_at? + datas.updated? + end + def updated_at - Time.at(@datas.updated.to_i / 1000) + Time.at(datas.updated.to_i / 1000) if updated_at? end - end diff --git a/app/models/export_report.rb b/app/models/export_report.rb new file mode 100644 index 000000000..1bbf1bd6a --- /dev/null +++ b/app/models/export_report.rb @@ -0,0 +1,113 @@ +class ExportReport + extend ActiveModel::Naming + include ActiveModel::Model + + attr_reader :datas, :errors, :metadatas + + def initialize( response ) + @datas = response.datas + @errors = response.errors + @metadatas = response.metadatas + end + + def zip_file + datas.zip_file + end + + def error_files + datas.files.select{ |file| file[:status] == "ERROR"} + end + + def ignored_files + datas.files.select{ |file| file[:status] == "IGNORED"} + end + + def ok_files + datas.files.select{ |file| file[:status] == "OK"} + end + + def files + datas.files + end + + def line_items + [].tap do |line_items| + datas.lines.each do |line| + line_items << LineItem.new(line) + end + end + end + + def lines + datas.stats.line_count if datas.stats_.line_count? + end + + def routes + datas.stats.route_count if datas.stats_.route_count? + end + + def connection_links + datas.stats.connection_link_count if datas.stats_.connection_link_count? + end + + def time_tables + datas.stats.time_table_count if datas.stats_.time_table_count? + end + + def stop_areas + datas.stats.stop_area_count if datas.stats_.stop_area_count? + end + + def access_points + datas.stats.access_point_count if datas.stats_.access_point_count? + end + + def vehicle_journeys + datas.stats.vehicle_journey_count if datas.stats_.vehicle_journey_count? + end + + def journey_patterns + datas.stats.journey_pattern_count if datas.stats_.journey_pattern_count? + end + + class LineItem + attr_reader :name, :status, :stats + + def initialize( options = Hashie::Mash.new ) + @name = options.name if options.name? + @status = options.status if options.status? + @stats = options.stats if options.stats? + end + + def routes + stats.route_count + end + + def connection_links + stats.connection_link_count + end + + def time_tables + stats.time_table_count + end + + def stop_areas + stats.stop_area_count + end + + def access_points + stats.access_point_count + end + + def vehicle_journeys + stats.vehicle_journey_count + end + + def journey_patterns + stats.journey_pattern_count + end + + end + +end + diff --git a/app/models/export_service.rb b/app/models/export_service.rb new file mode 100644 index 000000000..307ec34cd --- /dev/null +++ b/app/models/export_service.rb @@ -0,0 +1,23 @@ +class ExportService + + attr_reader :referential + + def initialize(referential) + @referential = referential + end + + # Find an export whith his id + def find(id) + Export.new(IevApi.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 new file mode 100644 index 000000000..09c0aeef4 --- /dev/null +++ b/app/models/export_task.rb @@ -0,0 +1,171 @@ +class ExportTask < ActiveRecord::Base + attr_accessor :resources, :rule_parameter_set_id + + belongs_to :referential + + has_one :user + has_one :compliance_check_task, :dependent => :delete + + serialize :parameter_set, JSON + serialize :result, JSON + + validates_presence_of :referential_id + validates_presence_of :resources + validates_presence_of :user_id + validates_presence_of :user_name + validates_inclusion_of :status, :in => %w{ pending processing completed failed } + + def references_types + [] + end + + protected + + def self.option(name, type=nil) + name = name.to_s + + define_method(name) do + self.parameter_set and self.parameter_set[name] + end + + if type.to_s == "boolean" + define_method("#{name}=") do |prefix| + (self.parameter_set ||= {})[name] = (prefix=="1" || prefix==true) + end + else + define_method("#{name}=") do |prefix| + (self.parameter_set ||= {})[name] = prefix + end + end + end + + public + + def self.formats + %w{Neptune Csv Gtfs Netex Hub} + end + + def self.format_label(format) + I18n.t 'exchange.format.'+format.downcase + end + + def delayed_export + delay.export + end + + def save_requested? + !parameter_set["no_save"] + end + + protected + + option :no_save, :boolean + option :format + option :file_path + option :references_type + + validates_inclusion_of :no_save, :in => [ true, false] + validates_inclusion_of :format, :in => self.formats + + def chouette_command + Chouette::Command.new(:schema => referential.slug) + end + + before_validation :define_default_attributes, :on => :create + def define_default_attributes + self.status ||= "pending" + end + + @@root = "#{Rails.root}/tmp/exports" + cattr_accessor :root + + def compliance_check_task_attributes + {:referential_id => referential.id, + :user_id => user_id, + :user_name => user_name, + :rule_parameter_set_id => rule_parameter_set_id} + end + + after_create :update_info, :save_resources + def update_info + self.file_path = saved_resources + self.update_attribute :parameter_set, self.parameter_set + + self.create_compliance_check_task( self.compliance_check_task_attributes) + end + + def save_resources + FileUtils.mkdir_p root + FileUtils.cp resources.path, saved_resources + end + + after_destroy :destroy_resources + def destroy_resources + FileUtils.rm file_path if File.exists? file_path + end + + def saved_resources + raise Exception.new("Illegal call") if self.new_record? + "#{root}/#{id}#{File.extname(resources.original_filename)}" + end + + def chouette_command_args + {:c => "export", :id => id} + end + + public + + def failed? + status == "failed" + end + + def completed? + status == "completed" + end + + def file_path_extension + extension = File.extname( self.file_path ) + if extension == ".xml" + "xml" + elsif extension == ".zip" + "zip" + else + "basic" + end + end + + def name + "#{ExportTask.model_name.human} #{self.format} #{self.id}" + end + + def full_name + return name unless no_save + "#{name} - #{I18n.t('activerecord.attributes.export_task.no_save')}" + end + + # Create ExportTask and ComplianceCheckTask associated and give export id to Chouette Loader + def export + return nil if self.new_record? + begin + chouette_command.run! chouette_command_args + reload + update_attribute :status, "completed" + compliance_check_task.update_attribute :status, "completed" + rescue => e + Rails.logger.error "Export #{id} failed : #{e}, #{e.backtrace}" + reload + update_attribute :status, "failed" + compliance_check_task.update_attribute :status, "failed" + end + end + + def self.new(attributes = {}, parameter_set = {}, &block) + if self == ExportTask + attributes[:format] = "Neptune" unless attributes[:format] + Object.const_get( attributes[:format] + "Export").new(attributes, parameter_set) + else + super + end + end + +end diff --git a/app/models/export_type.rb b/app/models/export_type.rb new file mode 100644 index 000000000..69b0e2367 --- /dev/null +++ b/app/models/export_type.rb @@ -0,0 +1,7 @@ +class ExportType < ActiveEnum::Base + value :id => 'Neptune', :name => 'Neptune' + value :id => 'Csv', :name => 'Csv' + value :id => 'Gtfs', :name => 'GTFS' + value :id => 'Netex', :name => 'NeTEx' + value :id => 'Hub', :name => 'HUB' +end diff --git a/db/migrate/20150319082515_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150319082515_change_collation_for_tag_names.acts_as_taggable_on_engine.rb new file mode 100644 index 000000000..bfb06bc7c --- /dev/null +++ b/db/migrate/20150319082515_change_collation_for_tag_names.acts_as_taggable_on_engine.rb @@ -0,0 +1,10 @@ +# This migration comes from acts_as_taggable_on_engine (originally 5) +# This migration is added to circumvent issue #623 and have special characters +# work properly +class ChangeCollationForTagNames < ActiveRecord::Migration + def up + if ActsAsTaggableOn::Utils.using_mysql? + execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;") + end + end +end |
