aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/application_controller.rb10
-rw-r--r--app/controllers/referentials_controller.rb12
-rw-r--r--app/controllers/vehicle_journeys_controller.rb2
-rw-r--r--app/decorators/referential_decorator.rb4
-rw-r--r--app/helpers/referentials_helper.rb10
-rw-r--r--app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js10
-rw-r--r--app/models/chouette/company.rb1
-rw-r--r--app/models/chouette/journey_pattern.rb16
-rw-r--r--app/models/chouette/line.rb9
-rw-r--r--app/models/chouette/purchase_window.rb1
-rw-r--r--app/models/chouette/route.rb6
-rw-r--r--app/models/chouette/stop_area.rb10
-rw-r--r--app/models/chouette/time_table.rb4
-rw-r--r--app/models/chouette/vehicle_journey.rb2
-rw-r--r--app/models/chouette/vehicle_journey_at_stop.rb8
-rw-r--r--app/models/chouette/vehicle_journey_at_stops_day_offset.rb16
-rw-r--r--app/models/organisation.rb4
-rw-r--r--app/models/referential.rb2
-rw-r--r--app/models/simple_importer.rb422
-rw-r--r--app/models/user.rb1
-rw-r--r--app/models/workbench.rb7
-rw-r--r--app/views/dashboards/_dashboard.html.slim30
-rw-r--r--app/views/layouts/navigation/_main_nav_left_content_stif.html.slim10
-rw-r--r--app/views/referentials/_form.html.slim4
-rw-r--r--app/views/referentials/select_compliance_control_set.html.slim2
-rw-r--r--app/views/stif/dashboards/_dashboard.html.slim6
-rw-r--r--app/views/workbenches/show.html.slim4
27 files changed, 552 insertions, 61 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 8bd3da2f9..45b7f55f6 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -36,16 +36,6 @@ class ApplicationController < ActionController::Base
end
helper_method :current_organisation
- def current_offer_workbench
- current_organisation.workbenches.find_by_name("Gestion de l'offre")
- end
- helper_method :current_offer_workbench
-
- def current_workgroup
- current_offer_workbench.workgroup
- end
- helper_method :current_workgroup
-
def current_functional_scope
functional_scope = current_organisation.sso_attributes.try(:[], "functional_scope") if current_organisation
JSON.parse(functional_scope) if functional_scope
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index 0ed3f75dd..5267c15d8 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -1,5 +1,6 @@
class ReferentialsController < ChouetteController
defaults :resource_class => Referential
+ before_action :load_workbench
include PolicyChecker
respond_to :html
@@ -30,7 +31,7 @@ class ReferentialsController < ChouetteController
def show
resource.switch
show! do |format|
- @referential = @referential.decorate(context: { current_workbench_id: params[:current_workbench_id] } )
+ @referential = @referential.decorate()
@reflines = lines_collection.paginate(page: params[:page], per_page: 10)
@reflines = ReferentialLineDecorator.decorate(
@reflines,
@@ -141,7 +142,6 @@ class ReferentialsController < ChouetteController
if params[:from]
source_referential = Referential.find(params[:from])
@referential = Referential.new_from(source_referential, current_functional_scope)
- @referential.workbench_id = params[:current_workbench_id]
end
@referential.data_format = current_organisation.data_format
@@ -175,4 +175,12 @@ class ReferentialsController < ChouetteController
)
end
+ def load_workbench
+ @workbench ||= Workbench.find(params[:workbench_id]) if params[:workbench_id]
+ @workbench ||= resource&.workbench if params[:id]
+ @workbench
+ end
+
+ alias_method :current_workbench, :load_workbench
+ helper_method :current_workbench
end
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index 9dbb3bc43..14795227c 100644
--- a/app/controllers/vehicle_journeys_controller.rb
+++ b/app/controllers/vehicle_journeys_controller.rb
@@ -154,7 +154,7 @@ class VehicleJourneysController < ChouetteController
private
def load_custom_fields
- @custom_fields = current_workgroup&.custom_fields_definitions || {}
+ @custom_fields = referential.workgroup&.custom_fields_definitions || {}
end
def map_stop_points points
diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb
index 3132cbf92..41cad237d 100644
--- a/app/decorators/referential_decorator.rb
+++ b/app/decorators/referential_decorator.rb
@@ -22,12 +22,12 @@ class ReferentialDecorator < AF83::Decorator
instance_decorator.action_link policy: :clone, secondary: :show do |l|
l.content t('actions.clone')
- l.href { h.new_referential_path(from: object.id, current_workbench_id: context[:current_workbench_id]) }
+ l.href { h.duplicate_workbench_referential_path(object) }
end
instance_decorator.action_link policy: :validate, secondary: :show do |l|
l.content t('actions.validate')
- l.href { h.referential_select_compliance_control_set_path(object.id) }
+ l.href { h.select_compliance_control_set_referential_path(object.id) }
end
instance_decorator.action_link policy: :archive, secondary: :show do |l|
diff --git a/app/helpers/referentials_helper.rb b/app/helpers/referentials_helper.rb
index 8251377aa..e464ec8a5 100644
--- a/app/helpers/referentials_helper.rb
+++ b/app/helpers/referentials_helper.rb
@@ -15,4 +15,14 @@ module ReferentialsHelper
service = ReferentialOverview.new referential, self
render partial: "referentials/overview", locals: {referential: referential, overview: service}
end
+
+ def mutual_workbench workbench
+ current_user.organisation.workbenches.where(workgroup_id: workbench.workgroup_id).last
+ end
+
+ def duplicate_workbench_referential_path referential
+ workbench = mutual_workbench referential.workbench
+ raise "Missing workbench for referential #{referential.name}" unless workbench.present?
+ new_workbench_referential_path(workbench, from: referential.id)
+ end
end
diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
index 7ab85a1ea..72dbd0152 100644
--- a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
+++ b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js
@@ -29,11 +29,11 @@ export default class BSelect4 extends Component {
val = this.props.selection.selectedJPModal
}
}
- if(this.useAjax()){
- val = val.published_name
- }
- else{
- if(val){
+ if(val){
+ if(this.useAjax()){
+ val = val.published_name
+ }
+ else{
val = val.id
}
}
diff --git a/app/models/chouette/company.rb b/app/models/chouette/company.rb
index b3d40ab96..53e412600 100644
--- a/app/models/chouette/company.rb
+++ b/app/models/chouette/company.rb
@@ -15,6 +15,5 @@ module Chouette
[:organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :url, :time_zone]
end
-
end
end
diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb
index aa9fdb810..830e985d9 100644
--- a/app/models/chouette/journey_pattern.rb
+++ b/app/models/chouette/journey_pattern.rb
@@ -170,5 +170,21 @@ module Chouette
end
full
end
+
+ def set_distances distances
+ raise "inconsistent data: #{distances.count} values for #{stop_points.count} stops" unless distances.count == stop_points.count
+ prev = distances[0].to_i
+ _costs = self.costs
+ distances[1..-1].each_with_index do |distance, i|
+ distance = distance.to_i
+ relative = distance - prev
+ prev = distance
+ start, stop = stop_points[i..i+1]
+ key = "#{start.stop_area_id}-#{stop.stop_area_id}"
+ _costs[key] ||= {}
+ _costs[key]["distance"] = relative
+ end
+ self.costs = _costs
+ end
end
end
diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb
index ba2e2755d..d077d5c9d 100644
--- a/app/models/chouette/line.rb
+++ b/app/models/chouette/line.rb
@@ -41,6 +41,7 @@ module Chouette
validates_presence_of :name
+
scope :by_text, ->(text) { where('lower(name) LIKE :t or lower(published_name) LIKE :t or lower(objectid) LIKE :t or lower(comment) LIKE :t or lower(number) LIKE :t',
t: "%#{text.downcase}%") }
@@ -80,6 +81,14 @@ module Chouette
line_referential.companies.where(id: ([company_id] + Array(secondary_company_ids)).compact)
end
+ def deactivate
+ self.deactivated = true
+ end
+
+ def activate
+ self.deactivated = false
+ end
+
def deactivate!
update_attribute :deactivated, true
end
diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb
index 334493015..157390a21 100644
--- a/app/models/chouette/purchase_window.rb
+++ b/app/models/chouette/purchase_window.rb
@@ -19,6 +19,7 @@ module Chouette
scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) }
scope :overlap_dates, ->(date_range) { where('daterange(?, ?) && any (date_ranges)', date_range.first, date_range.last + 1.day) }
+ scope :matching_dates, ->(date_range) { where('ARRAY[daterange(?, ?)] = date_ranges', date_range.first, date_range.last + 1.day) }
def self.ransackable_scopes(auth_object = nil)
[:contains_date]
diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb
index 5cc5d8b0d..3729deb7d 100644
--- a/app/models/chouette/route.rb
+++ b/app/models/chouette/route.rb
@@ -185,6 +185,12 @@ module Chouette
return true
end
+ def full_journey_pattern
+ journey_pattern = journey_patterns.find_or_create_by registration_number: self.number, name: self.name
+ journey_pattern.stop_points = self.stop_points
+ journey_pattern
+ end
+
protected
def self.vehicle_journeys_timeless(stop_point_id)
diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb
index bb8747faa..7170dd217 100644
--- a/app/models/chouette/stop_area.rb
+++ b/app/models/chouette/stop_area.rb
@@ -369,6 +369,14 @@ module Chouette
!activated?
end
+ def activate
+ self.deleted_at = nil
+ end
+
+ def deactivate
+ self.deleted_at = Time.now
+ end
+
def activate!
update_attribute :deleted_at, nil
end
@@ -384,8 +392,8 @@ module Chouette
def country_name
return unless country_code
-
country = ISO3166::Country[country_code]
+ return unless country
country.translations[I18n.locale.to_s] || country.name
end
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index 15b22b671..b76de852a 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -44,10 +44,10 @@ module Chouette
attrs << self.int_day_types
dates = self.dates
dates += TimeTableDate.where(time_table_id: self.id)
- attrs << dates.map(&:checksum).map(&:to_s).sort
+ attrs << dates.map(&:checksum).map(&:to_s).uniq.sort
periods = self.periods
periods += TimeTablePeriod.where(time_table_id: self.id)
- attrs << periods.map(&:checksum).map(&:to_s).sort
+ attrs << periods.map(&:checksum).map(&:to_s).uniq.sort
end
end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 1963797d2..6209993de 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -143,7 +143,7 @@ module Chouette
attrs << self.try(:company).try(:get_objectid).try(:local_id)
attrs << self.footnotes.map(&:checksum).sort
vjas = self.vehicle_journey_at_stops
- vjas += VehicleJourneyAtStop.where(vehicle_journey_id: self.id)
+ vjas += VehicleJourneyAtStop.where(vehicle_journey_id: self.id) unless self.new_record?
attrs << vjas.uniq.sort_by { |s| s.stop_point&.position }.map(&:checksum).sort
end
end
diff --git a/app/models/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb
index eda711ade..3b4f35f13 100644
--- a/app/models/chouette/vehicle_journey_at_stop.rb
+++ b/app/models/chouette/vehicle_journey_at_stop.rb
@@ -41,7 +41,7 @@ module Chouette
:arrival_day_offset,
I18n.t(
'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max',
- short_id: vehicle_journey.get_objectid.short_id,
+ short_id: vehicle_journey&.get_objectid&.short_id,
max: DAY_OFFSET_MAX + 1
)
)
@@ -52,7 +52,7 @@ module Chouette
:departure_day_offset,
I18n.t(
'vehicle_journey_at_stops.errors.day_offset_must_not_exceed_max',
- short_id: vehicle_journey.get_objectid.short_id,
+ short_id: vehicle_journey&.get_objectid&.short_id,
max: DAY_OFFSET_MAX + 1
)
)
@@ -69,8 +69,8 @@ module Chouette
def checksum_attributes
[].tap do |attrs|
- attrs << self.departure_time.try(:to_s, :time)
- attrs << self.arrival_time.try(:to_s, :time)
+ attrs << self.departure_time&.utc.try(:to_s, :time)
+ attrs << self.arrival_time&.utc.try(:to_s, :time)
attrs << self.departure_day_offset.to_s
attrs << self.arrival_day_offset.to_s
end
diff --git a/app/models/chouette/vehicle_journey_at_stops_day_offset.rb b/app/models/chouette/vehicle_journey_at_stops_day_offset.rb
index b2cb90d11..7497cd72c 100644
--- a/app/models/chouette/vehicle_journey_at_stops_day_offset.rb
+++ b/app/models/chouette/vehicle_journey_at_stops_day_offset.rb
@@ -11,13 +11,19 @@ module Chouette
@at_stops.inject(nil) do |prior_stop, stop|
next stop if prior_stop.nil?
- if stop.arrival_time < prior_stop.departure_time ||
- stop.arrival_time < prior_stop.arrival_time
+ # we only compare time of the day, not actual times
+ stop_arrival_time = stop.arrival_time - stop.arrival_time.to_date.to_time
+ stop_departure_time = stop.departure_time - stop.departure_time.to_date.to_time
+ prior_stop_arrival_time = prior_stop.arrival_time - prior_stop.arrival_time.to_date.to_time
+ prior_stop_departure_time = prior_stop.departure_time - prior_stop.departure_time.to_date.to_time
+
+ if stop_arrival_time < prior_stop_departure_time ||
+ stop_arrival_time < prior_stop_arrival_time
arrival_offset += 1
end
- if stop.departure_time < stop.arrival_time ||
- stop.departure_time < prior_stop.departure_time
+ if stop_departure_time < stop_arrival_time ||
+ stop_departure_time < prior_stop_departure_time
departure_offset += 1
end
@@ -39,4 +45,4 @@ module Chouette
save
end
end
-end \ No newline at end of file
+end
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index da7d1fcf3..e8fb4e060 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -80,4 +80,8 @@ class Organisation < ActiveRecord::Base
features && features.include?(feature.to_s)
end
+ def default_workbench
+ workbenches.default
+ end
+
end
diff --git a/app/models/referential.rb b/app/models/referential.rb
index 509e0412f..09c2e7d34 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -51,7 +51,9 @@ class Referential < ActiveRecord::Base
belongs_to :stop_area_referential
validates_presence_of :stop_area_referential
has_many :stop_areas, through: :stop_area_referential
+
belongs_to :workbench
+ delegate :workgroup, to: :workbench, allow_nil: true
belongs_to :referential_suite
diff --git a/app/models/simple_importer.rb b/app/models/simple_importer.rb
new file mode 100644
index 000000000..b824d596d
--- /dev/null
+++ b/app/models/simple_importer.rb
@@ -0,0 +1,422 @@
+class SimpleImporter < ActiveRecord::Base
+ attr_accessor :configuration
+
+ def self.define name
+ @importers ||= {}
+ configuration = Configuration.new name
+ yield configuration
+ configuration.validate!
+ @importers[name.to_sym] = configuration
+ end
+
+ def self.find_configuration name
+ @importers ||= {}
+ configuration = @importers[name.to_sym]
+ raise "Importer not found: #{name}" unless configuration
+ configuration
+ end
+
+ def initialize *args
+ super *args
+ self.configuration = self.class.find_configuration self.configuration_name
+ self.journal ||= []
+ end
+
+ def configure
+ new_config = configuration.duplicate
+ yield new_config
+ new_config.validate!
+ self.configuration = new_config
+ end
+
+ def context
+ self.configuration.context
+ end
+
+ def resolve col_name, value, &block
+ val = block.call(value)
+ return val if val.present?
+ @resolution_queue[[col_name.to_s, value]].push({record: @current_record, attribute: @current_attribute, block: block})
+ nil
+ end
+
+ def import opts={}
+ @verbose = opts.delete :verbose
+
+
+ @resolution_queue = Hash.new{|h,k| h[k] = []}
+ @errors = []
+ @messages = []
+ @number_of_lines = 0
+ @padding = 1
+ @current_line = 0
+ fail_with_error "File not found: #{self.filepath}" do
+ @number_of_lines = CSV.read(self.filepath, self.configuration.csv_options).length
+ @padding = [1, Math.log(@number_of_lines, 10).ceil()].max
+ end
+
+
+ self.configuration.before_actions(:parsing).each do |action| action.call self end
+
+ @statuses = ""
+
+ if ENV["NO_TRANSACTION"]
+ process_csv_file
+ else
+ ActiveRecord::Base.transaction do
+ process_csv_file
+ end
+ end
+ self.status ||= :success
+ rescue FailedImport
+ self.status = :failed
+ ensure
+ self.save!
+ end
+
+ def fail_with_error msg=nil, opts={}
+ begin
+ yield
+ rescue => e
+ msg = msg.call if msg.is_a?(Proc)
+ custom_print "\nFAILED: \n errors: #{msg}\n exception: #{e.message}\n#{e.backtrace.join("\n")}", color: :red unless self.configuration.ignore_failures
+ push_in_journal({message: msg, error: e.message, event: :error, kind: :error})
+ @new_status = colorize("x", :red)
+ if self.configuration.ignore_failures
+ raise FailedRow if opts[:abort_row]
+ else
+ raise FailedImport
+ end
+ end
+ end
+
+ def encode_string s
+ s.encode("utf-8").force_encoding("utf-8")
+ end
+
+ def dump_csv_from_context
+ filepath = "./#{self.configuration_name}_#{Time.now.strftime "%y%m%d%H%M"}.csv"
+ # for some reason, context[:csv].to_csv does not work
+ CSV.open(filepath, 'w') do |csv|
+ header = true
+ context[:csv].each do |row|
+ csv << row.headers if header
+ csv << row.fields
+ header = false
+ end
+ end
+ log "CSV file dumped in #{filepath}"
+ end
+
+ def log msg, opts={}
+ msg = colorize msg, opts[:color] if opts[:color]
+ if opts[:append]
+ @messages[-1] = (@messages[-1] || "") + msg
+ else
+ @messages << msg
+ end
+ print_state
+ end
+
+ protected
+
+ def process_csv_file
+ self.configuration.before_actions(:all).each do |action| action.call self end
+ log "Starting import ...", color: :green
+
+ (context[:csv] || CSV.read(filepath, self.configuration.csv_options)).each do |row|
+ @current_row = row
+ @new_status = nil
+ begin
+ handle_row row
+ fail_with_error ->(){ @current_record.errors.messages } do
+ new_record = @current_record&.new_record?
+ @new_status ||= new_record ? colorize("✓", :green) : colorize("-", :orange)
+ @event = new_record ? :creation : :update
+ self.configuration.before_actions(:each_save).each do |action|
+ action.call self, @current_record
+ end
+ ### This could fail if the record has a mandatory relation which is not yet resolved
+ ### TODO: do not attempt to save if the current record if waiting for resolution
+ ### and fail at the end if there remains unresolved relations
+ if @current_record
+ if self.configuration.ignore_failures
+ unless @current_record.save
+ @new_status = colorize("x", :red)
+ push_in_journal({message: "errors: #{@current_record.errors.messages}", error: "invalid record", event: :error, kind: :error})
+ end
+ else
+ @current_record.save!
+ end
+ end
+ self.configuration.after_actions(:each_save).each do |action|
+ action.call self, @current_record
+ end
+ end
+ rescue FailedRow
+ @new_status = colorize("x", :red)
+ end
+ push_in_journal({event: @event, kind: :log}) if @current_record&.valid?
+ @statuses += @new_status
+ self.configuration.columns.each do |col|
+ if @current_record && col.name && @resolution_queue.any?
+ val = @current_record.send col[:attribute]
+ (@resolution_queue.delete([col.name, val]) || []).each do |res|
+ record = res[:record]
+ attribute = res[:attribute]
+ value = res[:block].call(val, record)
+ record.send "#{attribute}=", value
+ record.save!
+ end
+ end
+ end
+ print_state
+ @current_line += 1
+ end
+
+ begin
+ self.configuration.after_actions(:all).each do |action|
+ action.call self
+ end
+ rescue FailedRow
+ end
+ end
+
+ def handle_row row
+ if self.configuration.get_custom_handler
+ instance_exec(row, &self.configuration.get_custom_handler)
+ else
+ fail_with_error "", abort_row: true do
+ @current_record = self.configuration.find_record row
+ self.configuration.columns.each do |col|
+ @current_attribute = col[:attribute]
+ val = col[:value]
+ if val.nil? || val.is_a?(Proc)
+ if row.has_key? col.name
+ if val.is_a?(Proc)
+ val = instance_exec(row[col.name], &val)
+ else
+ val = row[col.name]
+ end
+ else
+ push_in_journal({event: :column_not_found, message: "Column not found: #{col.name}", kind: :warning})
+ self.status ||= :success_with_warnings
+ end
+ end
+
+ if val.nil? && col.required?
+ raise "MISSING VALUE FOR COLUMN #{col.name}"
+ end
+ val = encode_string(val) if val.is_a?(String)
+ @current_record.send "#{@current_attribute}=", val if val
+ end
+ end
+ end
+ end
+
+ def push_in_journal data
+ line = @current_line + 1
+ line += 1 if configuration.headers
+ self.journal.push data.update(line: line, row: @current_row)
+ if data[:kind] == :error || data[:kind] == :warning
+ @errors.push data
+ end
+ end
+
+ def colorize txt, color
+ color = {
+ red: "31",
+ green: "32",
+ orange: "33",
+ }[color] || "33"
+ "\e[#{color}m#{txt}\e[0m"
+ end
+
+ def print_state
+ return unless @verbose
+
+ @status_width ||= begin
+ term_width = %x(tput cols).to_i
+ term_width - @padding - 10
+ rescue
+ 100
+ end
+
+ @status_height ||= begin
+ term_height = %x(tput lines).to_i
+ term_height - 3
+ rescue
+ 50
+ end
+
+ full_status = @statuses || ""
+ full_status = full_status.last(@status_width*10) || ""
+ padding_size = [(@number_of_lines - @current_line - 1), (@status_width - full_status.size/10)].min
+ full_status = "#{full_status}#{"."*[padding_size, 0].max}"
+
+ msg = "#{"%#{@padding}d" % (@current_line + 1)}/#{@number_of_lines}: #{full_status}"
+
+ lines_count = [(@status_height / 2) - 3, 1].max
+
+ if @messages.any?
+ msg += "\n\n"
+ msg += colorize "=== MESSAGES (#{@messages.count}) ===\n", :green
+ msg += "[...]\n" if @messages.count > lines_count
+ msg += @messages.last(lines_count).join("\n")
+ msg += "\n"*[lines_count-@messages.count, 0].max
+ end
+
+ if @errors.any?
+ msg += "\n\n"
+ msg += colorize "=== ERRORS (#{@errors.count}) ===\n", :red
+ msg += "[...]\n" if @errors.count > lines_count
+ msg += @errors.last(lines_count).map do |j|
+ kind = j[:kind]
+ kind = colorize(kind, kind == :error ? :red : :orange)
+ encode_string "[#{kind}]\t\tL#{j[:line]}\t#{j[:error]}\t\t#{j[:message]}"
+ end.join("\n")
+ end
+ custom_print msg, clear: true
+ end
+
+ def custom_print msg, opts={}
+ return unless @verbose
+ out = ""
+ msg = colorize(msg, opts[:color]) if opts[:color]
+ puts "\e[H\e[2J" if opts[:clear]
+ out += msg
+ print out
+ end
+
+ class FailedImport < RuntimeError
+ end
+
+ class FailedRow < RuntimeError
+ end
+
+ class Configuration
+ attr_accessor :model, :headers, :separator, :key, :context, :encoding, :ignore_failures, :scope
+ attr_reader :columns
+
+ def initialize import_name, opts={}
+ @import_name = import_name
+ @key = opts[:key] || "id"
+ @headers = opts.has_key?(:headers) ? opts[:headers] : true
+ @separator = opts[:separator] || ","
+ @encoding = opts[:encoding]
+ @columns = opts[:columns] || []
+ @model = opts[:model]
+ @custom_handler = opts[:custom_handler]
+ @before = opts[:before]
+ @after = opts[:after]
+ @ignore_failures = opts[:ignore_failures]
+ @context = opts[:context] || {}
+ @scope = opts[:scope]
+ end
+
+ def duplicate
+ Configuration.new @import_name, self.options
+ end
+
+ def options
+ {
+ key: @key,
+ headers: @headers,
+ separator: @separator,
+ encoding: @encoding,
+ columns: @columns.map(&:duplicate),
+ model: model,
+ custom_handler: @custom_handler,
+ before: @before,
+ after: @after,
+ ignore_failures: @ignore_failures,
+ context: @context,
+ scope: @scope
+ }
+ end
+
+ def validate!
+ raise "Incomplete configuration, missing model for #{@import_name}" unless model.present?
+ end
+
+ def attribute_for_col col_name
+ column = self.columns.find{|c| c.name == col_name}
+ column && column[:attribute] || col_name
+ end
+
+ def record_scope
+ _scope = @scope
+ _scope = instance_exec(&_scope) if _scope.is_a?(Proc)
+ _scope || model
+ end
+
+ def find_record attrs
+ record_scope.find_or_initialize_by(attribute_for_col(@key) => attrs[@key.to_s])
+ end
+
+ def csv_options
+ {
+ headers: self.headers,
+ col_sep: self.separator,
+ encoding: self.encoding
+ }
+ end
+
+ def add_column name, opts={}
+ @columns.push Column.new({name: name.to_s}.update(opts))
+ end
+
+ def add_value attribute, value
+ @columns.push Column.new({attribute: attribute, value: value})
+ end
+
+ def before group=:all, &block
+ @before ||= Hash.new{|h, k| h[k] = []}
+ @before[group].push block
+ end
+
+ def after group=:all, &block
+ @after ||= Hash.new{|h, k| h[k] = []}
+ @after[group].push block
+ end
+
+ def before_actions group=:all
+ @before ||= Hash.new{|h, k| h[k] = []}
+ @before[group]
+ end
+
+ def after_actions group=:all
+ @after ||= Hash.new{|h, k| h[k] = []}
+ @after[group]
+ end
+
+ def custom_handler &block
+ @custom_handler = block
+ end
+
+ def get_custom_handler
+ @custom_handler
+ end
+
+ class Column
+ attr_accessor :name
+ def initialize opts={}
+ @name = opts[:name]
+ @options = opts
+ @options[:attribute] ||= @name
+ end
+
+ def duplicate
+ Column.new @options.dup
+ end
+
+ def required?
+ !!@options[:required]
+ end
+
+ def [](key)
+ @options[key]
+ end
+ end
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 1342f60ed..31e634415 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,6 +15,7 @@ class User < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model
# attr_accessible :email, :password, :current_password, :password_confirmation, :remember_me, :name, :organisation_attributes
belongs_to :organisation
+ has_many :workbenches, through: :organisation
accepts_nested_attributes_for :organisation
validates :organisation, :presence => true
diff --git a/app/models/workbench.rb b/app/models/workbench.rb
index b80fa64ac..eb53af7aa 100644
--- a/app/models/workbench.rb
+++ b/app/models/workbench.rb
@@ -1,4 +1,6 @@
class Workbench < ActiveRecord::Base
+ DEFAULT_WORKBENCH_NAME = "Gestion de l'offre"
+
include ObjectidFormatterSupport
belongs_to :organisation
belongs_to :line_referential
@@ -40,6 +42,11 @@ class Workbench < ActiveRecord::Base
end
end
+ def self.default
+ self.last if self.count == 1
+ where(name: DEFAULT_WORKBENCH_NAME).last
+ end
+
private
def initialize_output
diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim
index 05257a766..7f78934a6 100644
--- a/app/views/dashboards/_dashboard.html.slim
+++ b/app/views/dashboards/_dashboard.html.slim
@@ -14,25 +14,25 @@
- if workbench.referentials.present?
.list-group
- workbench.referentials.limit(5).each do |referential|
- = link_to referential.name, referential_path(referential, workbench_id: referential.workbench_id, current_workbench_id: workbench.id), class: 'list-group-item'
+ = link_to referential.name, referential_path(referential), class: 'list-group-item'
- else
.panel-body
em.small.text-muted = t('workbenches.index.offers.no_content')
- .panel.panel-default
- .panel-heading
- h3.panel-title.with_actions
- = link_to I18n.t("activerecord.models.calendar", count: @dashboard.current_organisation.calendars.size), workgroup_calendars_path(current_workgroup)
- div
- = link_to '', workgroup_calendars_path(current_workgroup), class: ' fa fa-chevron-right pull-right'
- - if @dashboard.current_organisation.calendars.present?
- .list-group
- - @dashboard.current_organisation.calendars.order("updated_at desc").limit(5).each do |calendar|
- = link_to calendar.name, workgroup_calendars_path(current_workgroup, calendar), class: 'list-group-item'
- - else
- .panel-body
- em.small.text-muted
- = t('dasboard.calendars.none')
+ .panel.panel-default
+ .panel-heading
+ h3.panel-title.with_actions
+ = link_to I18n.t("activerecord.models.calendar", count: @dashboard.current_organisation.calendars.size), workgroup_calendars_path(workbench.workgroup)
+ div
+ = link_to '', workgroup_calendars_path(workbench.workgroup), class: ' fa fa-chevron-right pull-right'
+ - if @dashboard.current_organisation.calendars.present?
+ .list-group
+ - @dashboard.current_organisation.calendars.order("updated_at desc").limit(5).each do |calendar|
+ = link_to calendar.name, workgroup_calendars_path(workbench.workgroup, calendar), class: 'list-group-item'
+ - else
+ .panel-body
+ em.small.text-muted
+ = t('dasboard.calendars.none')
.col-lg-6.col-md-6.col-sm-6.col-xs-12
.panel.panel-default
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 1b7293d21..cb0698cf8 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
@@ -24,14 +24,14 @@
#miTwo.panel-collapse.collapse
.list-group
- - if current_user
- = link_to workbench_path(current_offer_workbench), class: "list-group-item #{params[:controller] == 'workbenches' ? 'active' : ''}" do
+ - current_user.workbenches.each do |current_workbench|
+ = link_to workbench_path(current_workbench), class: "list-group-item #{params[:controller] == 'workbenches' ? 'active' : ''}" do
span Jeux de données
- = link_to workbench_imports_path(current_offer_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do
+ = link_to workbench_imports_path(current_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do
span Import
- = link_to workgroup_calendars_path(current_workgroup), class: 'list-group-item' do
+ = 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_offer_workbench), class: 'list-group-item' do
+ = link_to workbench_compliance_check_sets_path(current_workbench), class: 'list-group-item' do
span Rapport de contrôle
= link_to compliance_control_sets_path, class: 'list-group-item' do
span Jeux de contrôle
diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim
index 9927f05bd..1e59ab566 100644
--- a/app/views/referentials/_form.html.slim
+++ b/app/views/referentials/_form.html.slim
@@ -1,4 +1,6 @@
-= simple_form_for @referential, html: {class: 'form-horizontal', id: 'referential_form'}, wrapper: :horizontal_form do |form|
+- url = @referential.new_record? ? [@workbench, @referential] : [@referential]
+
+= simple_form_for @referential, url: url, html: {class: 'form-horizontal', id: 'referential_form'}, wrapper: :horizontal_form do |form|
.row
.col-lg-12
diff --git a/app/views/referentials/select_compliance_control_set.html.slim b/app/views/referentials/select_compliance_control_set.html.slim
index 87a888c0a..69c87aab2 100644
--- a/app/views/referentials/select_compliance_control_set.html.slim
+++ b/app/views/referentials/select_compliance_control_set.html.slim
@@ -2,7 +2,7 @@
.container-fluid
.row
.col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1
- = form_tag validate_referential_path(params[:referential_id]), {class: 'form-horizontal', id: 'select_compliance_control_set', wrapper: :horizontal_form} do
+ = form_tag validate_referential_path(@referential), {class: 'form-horizontal', id: 'select_compliance_control_set', wrapper: :horizontal_form} do
.row
.col-lg-12
.form-group
diff --git a/app/views/stif/dashboards/_dashboard.html.slim b/app/views/stif/dashboards/_dashboard.html.slim
index c28696a94..83a2106bb 100644
--- a/app/views/stif/dashboards/_dashboard.html.slim
+++ b/app/views/stif/dashboards/_dashboard.html.slim
@@ -47,7 +47,7 @@
- if @dashboard.referentials.present?
.list-group
- @dashboard.referentials.first(5).each_with_index do |referential, i|
- = link_to referential.name, referential_path(referential, workbench_id: referential.workbench_id, current_workbench_id: @dashboard.workbench.id), class: 'list-group-item' if i < 6
+ = link_to referential.name, referential_path(referential), class: 'list-group-item' if i < 6
- else
.panel-body
@@ -60,12 +60,12 @@
span.badge.ml-xs = @dashboard.calendars.count if @dashboard.calendars.present?
div
- = link_to '', workgroup_calendars_path(current_workgroup), class: ' fa fa-chevron-right pull-right', title: t('.see')
+ = link_to '', workgroup_calendars_path(@dashboard.workbench.workgroup), class: ' fa fa-chevron-right pull-right', title: t('.see')
- if @dashboard.calendars.present?
.list-group
- @dashboard.calendars.first(5).each_with_index do |calendar, i|
- = link_to calendar.name, workgroup_calendar_path(current_workgroup, calendar), class: 'list-group-item' if i < 6
+ = link_to calendar.name, workgroup_calendar_path(@dashboard.workbench.workgroup, calendar), class: 'list-group-item' if i < 6
- else
.panel-body
diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim
index aae34c51b..159aa8ea2 100644
--- a/app/views/workbenches/show.html.slim
+++ b/app/views/workbenches/show.html.slim
@@ -5,7 +5,7 @@
.col-lg-12.text-right
- if policy(Referential).create?
= link_to t('actions.import'), workbench_imports_path(@workbench), class: 'btn btn-primary'
- = link_to t('actions.add'), new_referential_path(workbench_id: @workbench), class: 'btn btn-primary'
+ = link_to t('actions.add'), new_workbench_referential_path(@workbench), class: 'btn btn-primary'
= link_to t('workbenches.actions.show_output'), workbench_output_path(@workbench), class: 'btn btn-primary'
.page_content
@@ -25,7 +25,7 @@
key: :name, \
attribute: 'name', \
link_to: lambda do |referential| \
- referential_path(referential, current_workbench_id: params[:id]) \
+ referential_path(referential) \
end \
), \
TableBuilderHelper::Column.new( \