diff options
Diffstat (limited to 'app/models')
65 files changed, 514 insertions, 111 deletions
| diff --git a/app/models/api/v1/api_key.rb b/app/models/api/v1/api_key.rb index 09c6f77ac..e6ceb977a 100644 --- a/app/models/api/v1/api_key.rb +++ b/app/models/api/v1/api_key.rb @@ -1,7 +1,8 @@  module Api    module V1 -    class ApiKey < ::ActiveRecord::Base -      has_paper_trail +    class ApiKey < ::ApplicationModel +      has_metadata +              before_create :generate_access_token        belongs_to :referential, :class_name => '::Referential'        belongs_to :organisation, :class_name => '::Organisation' @@ -47,4 +48,3 @@ module Api      end    end  end - diff --git a/app/models/application_model.rb b/app/models/application_model.rb new file mode 100644 index 000000000..1a2a5099d --- /dev/null +++ b/app/models/application_model.rb @@ -0,0 +1,5 @@ +class ApplicationModel < ::ActiveRecord::Base +  include MetadataSupport + +  self.abstract_class = true +end diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 32eedf9ea..39e2b2cff 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -2,13 +2,13 @@ require 'range_ext'  require_relative 'calendar/date_value'  require_relative 'calendar/period' -class Calendar < ActiveRecord::Base +class Calendar < ApplicationModel    include DateSupport    include PeriodSupport    include ApplicationDaysSupport    include TimetableSupport -  has_paper_trail class_name: 'PublicVersion' +  has_metadata    belongs_to :organisation    belongs_to :workgroup diff --git a/app/models/calendar/date_value.rb b/app/models/calendar/date_value.rb index a4a405d43..f50b4237c 100644 --- a/app/models/calendar/date_value.rb +++ b/app/models/calendar/date_value.rb @@ -1,4 +1,4 @@ -class Calendar < ActiveRecord::Base +class Calendar < ApplicationModel    class DateValue      include ActiveAttr::Model diff --git a/app/models/calendar/period.rb b/app/models/calendar/period.rb index 8b3e4109b..07926e818 100644 --- a/app/models/calendar/period.rb +++ b/app/models/calendar/period.rb @@ -1,4 +1,4 @@ -class Calendar < ActiveRecord::Base +class Calendar < ApplicationModel    class Period      include ActiveAttr::Model diff --git a/app/models/chouette/access_link.rb b/app/models/chouette/access_link.rb index 6b08443be..7ab8ca715 100644 --- a/app/models/chouette/access_link.rb +++ b/app/models/chouette/access_link.rb @@ -1,6 +1,6 @@  module Chouette    class AccessLink < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      include ObjectidSupport      attr_accessor :access_link_type, :link_orientation_type, :link_key diff --git a/app/models/chouette/access_point.rb b/app/models/chouette/access_point.rb index ac6580015..884460881 100644 --- a/app/models/chouette/access_point.rb +++ b/app/models/chouette/access_point.rb @@ -4,7 +4,7 @@ require 'geo_ruby'  module Chouette    class AccessPoint < Chouette::ActiveRecord -    has_paper_trail +    has_metadata      include Geokit::Mappable      include ProjectionFields diff --git a/app/models/chouette/active_record.rb b/app/models/chouette/active_record.rb index c2aab9d50..27f5426b3 100644 --- a/app/models/chouette/active_record.rb +++ b/app/models/chouette/active_record.rb @@ -1,7 +1,8 @@  #require "active_record"  require 'deep_cloneable'  module Chouette -  class ActiveRecord < ::ActiveRecord::Base +  class ActiveRecord < ::ApplicationModel +      self.abstract_class = true      before_save :nil_if_blank, :set_data_source_ref diff --git a/app/models/chouette/company.rb b/app/models/chouette/company.rb index f5ed4e0f8..9d5737a6c 100644 --- a/app/models/chouette/company.rb +++ b/app/models/chouette/company.rb @@ -1,9 +1,11 @@  module Chouette    class Company < Chouette::ActiveRecord +    has_metadata +      include CompanyRestrictions      include LineReferentialSupport      include ObjectidSupport -    has_paper_trail class_name: 'PublicVersion' +    include CustomFieldsSupport      has_many :lines diff --git a/app/models/chouette/connection_link.rb b/app/models/chouette/connection_link.rb index c53d6f5f1..fb93e5f90 100644 --- a/app/models/chouette/connection_link.rb +++ b/app/models/chouette/connection_link.rb @@ -1,6 +1,6 @@  module Chouette    class ConnectionLink < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      include ObjectidSupport      include ConnectionLinkRestrictions diff --git a/app/models/chouette/for_alighting_enumerations.rb b/app/models/chouette/for_alighting_enumerations.rb index ab07a670d..2e15fcb58 100644 --- a/app/models/chouette/for_alighting_enumerations.rb +++ b/app/models/chouette/for_alighting_enumerations.rb @@ -3,6 +3,6 @@ module Chouette      extend Enumerize      extend ActiveModel::Naming -    enumerize :for_alighting, in: %w[normal forbidden request_stop is_flexible] +    enumerize :for_alighting, in: %w[normal forbidden request_stop is_flexible], default: :normal    end  end diff --git a/app/models/chouette/for_boarding_enumerations.rb b/app/models/chouette/for_boarding_enumerations.rb index 48f8762c2..0190bf805 100644 --- a/app/models/chouette/for_boarding_enumerations.rb +++ b/app/models/chouette/for_boarding_enumerations.rb @@ -3,6 +3,6 @@ module Chouette      extend Enumerize      extend ActiveModel::Naming -    enumerize :for_boarding, in: %w[normal forbidden request_stop is_flexible] +    enumerize :for_boarding, in: %w[normal forbidden request_stop is_flexible], default: :normal    end  end diff --git a/app/models/chouette/group_of_line.rb b/app/models/chouette/group_of_line.rb index 3b6a7cea7..a30c34ce7 100644 --- a/app/models/chouette/group_of_line.rb +++ b/app/models/chouette/group_of_line.rb @@ -1,6 +1,6 @@  module Chouette    class GroupOfLine < Chouette::ActiveRecord -    has_paper_trail +    has_metadata      include ObjectidSupport      include GroupOfLineRestrictions      include LineReferentialSupport diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb index 5a5132200..830a6a808 100644 --- a/app/models/chouette/journey_pattern.rb +++ b/app/models/chouette/journey_pattern.rb @@ -1,6 +1,6 @@  module Chouette    class JourneyPattern < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      include ChecksumSupport      include JourneyPatternRestrictions      include ObjectidSupport diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb index 9f05b611a..4b5d1a68d 100644 --- a/app/models/chouette/line.rb +++ b/app/models/chouette/line.rb @@ -1,6 +1,6 @@  module Chouette    class Line < Chouette::ActiveRecord -    has_paper_trail class_name: 'PublicVersion' +    has_metadata      include LineRestrictions      include LineReferentialSupport      include ObjectidSupport @@ -51,6 +51,14 @@ module Chouette          )      } +    scope :for_organisation, ->(organisation){ +      if objectids = organisation&.lines_scope +        where(objectid: objectids) +      else +        all +      end +    } +      def self.nullable_attributes        [:published_name, :number, :comment, :url, :color, :text_color, :stable_id]      end diff --git a/app/models/chouette/network.rb b/app/models/chouette/network.rb index 942fc5d67..4802d7592 100644 --- a/app/models/chouette/network.rb +++ b/app/models/chouette/network.rb @@ -1,6 +1,6 @@  module Chouette    class Network < Chouette::ActiveRecord -    has_paper_trail class_name: 'PublicVersion' +    has_metadata      include NetworkRestrictions      include LineReferentialSupport      include ObjectidSupport diff --git a/app/models/chouette/pt_link.rb b/app/models/chouette/pt_link.rb index 399539d44..680632a14 100644 --- a/app/models/chouette/pt_link.rb +++ b/app/models/chouette/pt_link.rb @@ -2,7 +2,7 @@ require 'geokit'  module Chouette    class PtLink < Chouette::ActiveRecord -    has_paper_trail +    has_metadata      include Geokit::Mappable      def geometry diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb index 4c8014780..e10b106ec 100644 --- a/app/models/chouette/purchase_window.rb +++ b/app/models/chouette/purchase_window.rb @@ -11,7 +11,7 @@ module Chouette      enumerize :color, in: %w(#9B9B9B #FFA070 #C67300 #7F551B #41CCE3 #09B09C #3655D7 #6321A0 #E796C6 #DD2DAA) -    has_paper_trail +    has_metadata      belongs_to :referential      has_and_belongs_to_many :vehicle_journeys, :class_name => 'Chouette::VehicleJourney' diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 13288bc6b..9c7a3e6d9 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -1,6 +1,7 @@  module Chouette    class Route < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata +      include RouteRestrictions      include ChecksumSupport      include ObjectidSupport @@ -9,7 +10,6 @@ module Chouette      enumerize :direction, in: %i(straight_forward backward clockwise counter_clockwise north north_west west south_west south south_east east north_east)      enumerize :wayback, in: %i(outbound inbound), default: :outbound -      def self.nullable_attributes        [:published_name, :comment, :number, :name, :direction, :wayback]      end @@ -68,28 +68,34 @@ module Chouette      validates_presence_of :published_name      validates_presence_of :line      validates :wayback, inclusion: { in: self.wayback.values } -      after_save :calculate_costs!, if: ->() { TomTom.enabled? } - -    def duplicate +     +    def duplicate opposite=false        overrides = {          'opposite_route_id' => nil,          'name' => I18n.t('activerecord.copy', name: self.name)        } +      keys_for_create = attributes.keys - %w{id objectid created_at updated_at}        atts_for_create = attributes -        .slice!(*%w{id objectid created_at updated_at}) +        .slice(*keys_for_create)          .merge(overrides) +      if opposite +        atts_for_create[:wayback] = self.opposite_wayback +        atts_for_create[:name] = I18n.t('routes.opposite', name: self.name) +        atts_for_create[:published_name] = atts_for_create[:name] +        atts_for_create[:opposite_route_id] = self.id +      end        new_route = self.class.create!(atts_for_create) -      duplicate_stop_points(for_route: new_route) +      duplicate_stop_points(for_route: new_route, opposite: opposite)        new_route      end -    def duplicate_stop_points(for_route:) -      stop_points.each(&duplicate_stop_point(for_route: for_route)) +    def duplicate_stop_points(for_route:, opposite: false) +      stop_points.each(&duplicate_stop_point(for_route: for_route, opposite: opposite))      end -    def duplicate_stop_point(for_route:) +    def duplicate_stop_point(for_route:, opposite: false)        -> stop_point do -        stop_point.duplicate(for_route: for_route) +        stop_point.duplicate(for_route: for_route, opposite: opposite)        end      end diff --git a/app/models/chouette/routing_constraint_zone.rb b/app/models/chouette/routing_constraint_zone.rb index 58703598e..886eadc6c 100644 --- a/app/models/chouette/routing_constraint_zone.rb +++ b/app/models/chouette/routing_constraint_zone.rb @@ -1,6 +1,6 @@  module Chouette    class RoutingConstraintZone < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      include ChecksumSupport      include ObjectidSupport diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index 5f2c92acb..4ddc7403b 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -2,11 +2,12 @@ require 'geokit'  require 'geo_ruby'  module Chouette    class StopArea < Chouette::ActiveRecord -    has_paper_trail class_name: 'PublicVersion' +    has_metadata      include ProjectionFields      include StopAreaRestrictions      include StopAreaReferentialSupport      include ObjectidSupport +    include CustomFieldsSupport      extend Enumerize      enumerize :area_type, in: Chouette::AreaType::ALL @@ -49,7 +50,7 @@ module Chouette      validate :registration_number_is_set      before_validation do -      self.registration_number ||= self.stop_area_referential.generate_registration_number +      self.registration_number = self.stop_area_referential.generate_registration_number unless self.registration_number.present?      end      def self.nullable_attributes @@ -90,7 +91,7 @@ module Chouette        end        unless self.stop_area_referential.validates_registration_number(self.registration_number) -        errors.add(:registration_number, I18n.t('stop_areas.errors.registration_number.invalid')) +        errors.add(:registration_number, I18n.t('stop_areas.errors.registration_number.invalid', mask: self.stop_area_referential.registration_number_format))        end      end diff --git a/app/models/chouette/stop_point.rb b/app/models/chouette/stop_point.rb index 6b363cd93..1df1a664a 100644 --- a/app/models/chouette/stop_point.rb +++ b/app/models/chouette/stop_point.rb @@ -1,6 +1,6 @@  module Chouette    class StopPoint < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      def self.policy_class        RoutePolicy      end @@ -39,11 +39,12 @@ module Chouette        end      end -    def duplicate(for_route:) +    def duplicate(for_route:, opposite: false)        keys_for_create = attributes.keys - %w{id objectid created_at updated_at}        atts_for_create = attributes          .slice(*keys_for_create)          .merge('route_id' => for_route.id) +      atts_for_create["position"] = self.route.stop_points.size - atts_for_create["position"] if opposite        self.class.create!(atts_for_create)      end diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 506e498b8..b59c95665 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -1,6 +1,6 @@  module Chouette    class TimeTable < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      include ChecksumSupport      include TimeTableRestrictions      include ObjectidSupport diff --git a/app/models/chouette/timeband.rb b/app/models/chouette/timeband.rb index 5a4e17b98..38260b755 100644 --- a/app/models/chouette/timeband.rb +++ b/app/models/chouette/timeband.rb @@ -9,7 +9,7 @@ module Chouette    class Timeband < Chouette::TridentActiveRecord      include ObjectidSupport -    has_paper_trail +    has_metadata      validates :start_time, :end_time, presence: true      validates_with Chouette::TimebandValidator diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 525036077..54aad290c 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -1,7 +1,7 @@  # coding: utf-8  module Chouette    class VehicleJourney < Chouette::TridentActiveRecord -    has_paper_trail +    has_metadata      include ChecksumSupport      include CustomFieldsSupport      include VehicleJourneyRestrictions @@ -346,6 +346,33 @@ module Chouette        end      end +    def fill_passing_time_at_borders +      encountered_borders = [] +      previous_stop = nil +      vehicle_journey_at_stops.each do |vjas| +        sp = vjas.stop_point +        if sp.stop_area.area_type == "border" +          encountered_borders << vjas +        else +          if encountered_borders.any? +            before_cost = journey_pattern.costs_between previous_stop.stop_point, encountered_borders.first.stop_point +            after_cost = journey_pattern.costs_between encountered_borders.last.stop_point, sp +            if before_cost && before_cost[:distance] && after_cost && after_cost[:distance] +              before_distance = before_cost[:distance].to_f +              after_distance = after_cost[:distance].to_f +              time = previous_stop.departure_time + before_distance / (before_distance+after_distance) * (vjas.arrival_time - previous_stop.departure_time) +              encountered_borders.each do |b| +                b.update_attribute :arrival_time, time +                b.update_attribute :departure_time, time +              end +            end +            encountered_borders = [] +          end +          previous_stop = vjas +        end +      end +    end +      def self.matrix(vehicle_journeys)        Hash[*VehicleJourneyAtStop.where(vehicle_journey_id: vehicle_journeys.pluck(:id)).map do |vjas|          [ "#{vjas.vehicle_journey_id}-#{vjas.stop_point_id}", vjas] diff --git a/app/models/clean_up.rb b/app/models/clean_up.rb index 7aab7f32e..ec47489e9 100644 --- a/app/models/clean_up.rb +++ b/app/models/clean_up.rb @@ -1,4 +1,4 @@ -class CleanUp < ActiveRecord::Base +class CleanUp < ApplicationModel    extend Enumerize    include AASM    belongs_to :referential diff --git a/app/models/clean_up_result.rb b/app/models/clean_up_result.rb index 24d262deb..dff4f5acd 100644 --- a/app/models/clean_up_result.rb +++ b/app/models/clean_up_result.rb @@ -1,3 +1,3 @@ -class CleanUpResult < ActiveRecord::Base +class CleanUpResult < ApplicationModel    belongs_to :clean_up  end diff --git a/app/models/compliance_check.rb b/app/models/compliance_check.rb index 9d817e146..4ef6170e9 100644 --- a/app/models/compliance_check.rb +++ b/app/models/compliance_check.rb @@ -1,4 +1,4 @@ -class ComplianceCheck < ActiveRecord::Base +class ComplianceCheck < ApplicationModel    include ComplianceItemSupport    self.inheritance_column = nil diff --git a/app/models/compliance_check_block.rb b/app/models/compliance_check_block.rb index 059547e1b..e4f4c1c37 100644 --- a/app/models/compliance_check_block.rb +++ b/app/models/compliance_check_block.rb @@ -1,4 +1,4 @@ -class ComplianceCheckBlock < ActiveRecord::Base +class ComplianceCheckBlock < ApplicationModel    include StifTransportModeEnumerations    include StifTransportSubmodeEnumerations diff --git a/app/models/compliance_check_message.rb b/app/models/compliance_check_message.rb index 738bd4a4b..a4b1062f6 100644 --- a/app/models/compliance_check_message.rb +++ b/app/models/compliance_check_message.rb @@ -1,4 +1,4 @@ -class ComplianceCheckMessage < ActiveRecord::Base +class ComplianceCheckMessage < ApplicationModel    extend Enumerize    belongs_to :compliance_check_set diff --git a/app/models/compliance_check_resource.rb b/app/models/compliance_check_resource.rb index 777254aaf..d2f782e2b 100644 --- a/app/models/compliance_check_resource.rb +++ b/app/models/compliance_check_resource.rb @@ -1,4 +1,4 @@ -class ComplianceCheckResource < ActiveRecord::Base +class ComplianceCheckResource < ApplicationModel    extend Enumerize    belongs_to :compliance_check_set diff --git a/app/models/compliance_check_set.rb b/app/models/compliance_check_set.rb index 49d324c53..8b1dbdd68 100644 --- a/app/models/compliance_check_set.rb +++ b/app/models/compliance_check_set.rb @@ -1,6 +1,7 @@ -class ComplianceCheckSet < ActiveRecord::Base +class ComplianceCheckSet < ApplicationModel    extend Enumerize -  has_paper_trail class_name: 'PublicVersion' + +  has_metadata    belongs_to :referential    belongs_to :compliance_control_set diff --git a/app/models/compliance_control.rb b/app/models/compliance_control.rb index 1cc06f927..672fb128c 100644 --- a/app/models/compliance_control.rb +++ b/app/models/compliance_control.rb @@ -1,4 +1,4 @@ -class ComplianceControl < ActiveRecord::Base +class ComplianceControl < ApplicationModel    include ComplianceItemSupport    class << self diff --git a/app/models/compliance_control_block.rb b/app/models/compliance_control_block.rb index d7d84fd06..6a3c8a34e 100644 --- a/app/models/compliance_control_block.rb +++ b/app/models/compliance_control_block.rb @@ -1,4 +1,4 @@ -class ComplianceControlBlock < ActiveRecord::Base +class ComplianceControlBlock < ApplicationModel    include StifTransportModeEnumerations    include StifTransportSubmodeEnumerations @@ -12,6 +12,8 @@ class ComplianceControlBlock < ActiveRecord::Base    validates :transport_mode, presence: true    validates :compliance_control_set, presence: true +  validates_uniqueness_of :condition_attributes, scope: :compliance_control_set_id +    def name      ApplicationController.helpers.transport_mode_text(self)    end diff --git a/app/models/compliance_control_set.rb b/app/models/compliance_control_set.rb index c0ea692f2..4f0f86d08 100644 --- a/app/models/compliance_control_set.rb +++ b/app/models/compliance_control_set.rb @@ -1,5 +1,6 @@ -class ComplianceControlSet < ActiveRecord::Base -  has_paper_trail class_name: 'PublicVersion' +class ComplianceControlSet < ApplicationModel +  has_metadata +    belongs_to :organisation    has_many :compliance_control_blocks, dependent: :destroy    has_many :compliance_controls, dependent: :destroy diff --git a/app/models/concerns/custom_fields_support.rb b/app/models/concerns/custom_fields_support.rb index 6c76bd653..017f496a8 100644 --- a/app/models/concerns/custom_fields_support.rb +++ b/app/models/concerns/custom_fields_support.rb @@ -3,22 +3,51 @@ module CustomFieldsSupport    included do      validate :custom_fields_values_are_valid +    after_initialize :initialize_custom_fields -    def self.custom_fields -      CustomField.where(resource_type: self.name.split("::").last) +    def self.custom_fields workgroup=:all +      fields = CustomField.where(resource_type: self.name.split("::").last) +      fields = fields.where(workgroup_id: workgroup&.id) if workgroup != :all +      fields      end -    def custom_fields -      CustomField::Collection.new self +    def method_missing method_name, *args +      if method_name =~ /custom_field_*/ && method_name.to_sym != :custom_field_values && !@custom_fields_initialized +        initialize_custom_fields +        send method_name, *args +      else +        super method_name, *args +      end +    end + +    def custom_fields workgroup=:all +      CustomField::Collection.new self, workgroup +    end + +    def custom_field_values= vals +      out = {} +      custom_fields.each do |code, field| +        out[code] = field.preprocess_value_for_assignment(vals.symbolize_keys[code.to_sym]) +      end +      write_attribute :custom_field_values, out +    end + +    def initialize_custom_fields +      self.custom_field_values ||= {} +      custom_fields(:all).values.each &:initialize_custom_field +      custom_fields(:all).each do |k, v| +        custom_field_values[k] ||= v.default_value +      end +      @custom_fields_initialized = true      end      def custom_field_value key -      (custom_field_values || {})[key.to_s] +      (custom_field_values&.stringify_keys || {})[key.to_s]      end      private      def custom_fields_values_are_valid -      custom_fields.values.all?{|cf| cf.valid?} +      custom_fields(:all).values.all?{|cf| cf.valid?}      end    end  end diff --git a/app/models/concerns/metadata_support.rb b/app/models/concerns/metadata_support.rb new file mode 100644 index 000000000..c4bedbcda --- /dev/null +++ b/app/models/concerns/metadata_support.rb @@ -0,0 +1,107 @@ +module MetadataSupport +  extend ActiveSupport::Concern + +  included do +    class << self +      def has_metadata? +        !!@has_metadata +      end + +      def has_metadata opts={} +        @has_metadata = true + +        define_method :metadata do +          attr_name = opts[:attr_name] || :metadata +          @wrapped_metadata ||= begin +            wrapped = MetadataSupport::MetadataWrapper.new self.read_attribute(attr_name) +            wrapped.attribute_name = attr_name +            wrapped.owner = self +            wrapped +          end +        end + +        define_method :metadata= do |val| +          @wrapped_metadata = nil +          super val +        end + +        define_method :set_metadata! do |name, value| +          self.metadata.send "#{name}=", value +          self.save! +        end +      end +    end +  end + +  def has_metadata? +    self.class.has_metadata? +  end + +  def merge_metadata_from source +    return unless source.has_metadata? +    source_metadata = source.metadata +    res = {} +    self.metadata.each do |k, v| +      unless self.metadata.is_timestamp_attr?(k) +        ts = self.metadata.timestamp_attr(k) +        if source_metadata[ts] && source_metadata[ts] > self.metadata[ts] +          res[k] = source_metadata[k] +        else +          res[k] = v +        end +      end +    end +    self.metadata = res +    self +  end + +  class MetadataWrapper < OpenStruct +    attr_accessor :attribute_name, :owner + +    def is_timestamp_attr? name +      name =~ /_updated_at$/ +    end + +    def timestamp_attr name +      "#{name}_updated_at".to_sym +    end + +    def method_missing(mid, *args) +      out = super(mid, *args) +      owner.send :write_attribute, attribute_name, @table +      out = out&.to_time if args.length == 0 && is_timestamp_attr?(mid) +      out +    end + +    def each +      @table.each do |k,v| +        yield k, v +      end +    end + +    def new_ostruct_member name +      unless is_timestamp_attr?(name) +        timestamp_attr_name = timestamp_attr(name) +      end + +      name = name.to_sym +      unless respond_to?(name) +        if timestamp_attr_name +          define_singleton_method(timestamp_attr_name) { @table[timestamp_attr_name]&.to_time } +          define_singleton_method(name) { @table[name] } +        else +          # we are defining an accessor for a timestamp +          define_singleton_method(name) { @table[name]&.to_time } +        end + +        define_singleton_method("#{name}=") do |x| +          modifiable[timestamp_attr_name] = Time.now if timestamp_attr_name +          modifiable[name] = x +          owner.send :write_attribute, attribute_name, @table +        end +        modifiable[timestamp_attr_name] = Time.now if timestamp_attr_name +      end +      name +    end +  end +end diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 402df7fa9..22118a15a 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -1,16 +1,20 @@ -class CustomField < ActiveRecord::Base +class CustomField < ApplicationModel    extend Enumerize    belongs_to :workgroup -  enumerize :field_type, in: %i{list} +  enumerize :field_type, in: %i{list integer string attachment}    validates :name, uniqueness: {scope: [:resource_type, :workgroup_id]} -  validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false} +  validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false}, presence: true + +  scope :for_workgroup, ->(workgroup){ where workgroup_id: workgroup.id } + +  scope :for_workgroup, ->(workgroup){ where workgroup_id: workgroup.id }    class Collection < HashWithIndifferentAccess -    def initialize object -      vals = object.class.custom_fields.map do |v| -        [v.code, CustomField::Value.new(object, v, object.custom_field_value(v.code))] +    def initialize object, workgroup=:all +      vals = object.class.custom_fields(workgroup).map do |v| +        [v.code, CustomField::Instance.new(object, v, object.custom_field_value(v.code))]        end        super Hash[*vals.flatten]      end @@ -20,11 +24,11 @@ class CustomField < ActiveRecord::Base      end    end -  class Value +  class Instance      def self.new owner, custom_field, value -      field_type = custom_field.options["field_type"] -      klass_name = field_type && "CustomField::Value::#{field_type.classify}" -      klass = klass_name && const_defined?(klass_name) ? klass_name.constantize : CustomField::Value::Base +      field_type = custom_field.field_type +      klass_name = field_type && "CustomField::Instance::#{field_type.classify}" +      klass = klass_name.safe_constantize || CustomField::Instance::Base        klass.new owner, custom_field, value      end @@ -38,8 +42,14 @@ class CustomField < ActiveRecord::Base          @valid = false        end +      attr_accessor :owner, :custom_field +        delegate :code, :name, :field_type, to: :@custom_field +      def default_value +        options["default"] +      end +        def options          @custom_field.options || {}        end @@ -57,6 +67,14 @@ class CustomField < ActiveRecord::Base          @raw_value        end +      def input form_helper +        @input ||= begin +          klass_name = field_type && "CustomField::Instance::#{field_type.classify}::Input" +          klass = klass_name.safe_constantize || CustomField::Instance::Base::Input +          klass.new self, form_helper +        end +      end +        def errors_key          "custom_fields.#{code}"        end @@ -64,22 +82,184 @@ class CustomField < ActiveRecord::Base        def to_hash          HashWithIndifferentAccess[*%w(code name field_type options value).map{|k| [k, send(k)]}.flatten(1)]        end + +      def display_value +        value +      end + +      def initialize_custom_field +      end + +      def preprocess_value_for_assignment val +        val +      end + +      def render_partial +        ActionView::Base.new(Rails.configuration.paths["app/views"].first).render( +          :partial => "shared/custom_fields/#{field_type}", +          :locals => { field: self} +        ) +      end + +      class Input +        def initialize instance, form_helper +          @instance = instance +          @form_helper = form_helper +        end + +        def custom_field +          @instance.custom_field +        end + +        delegate :custom_field, :value, :options, to: :@instance +        delegate :code, :name, :field_type, to: :custom_field + +        def to_s +          out = form_input +          out.html_safe +        end + +        protected + +        def form_input_id +          "custom_field_#{code}" +        end + +        def form_input_name +          "#{@form_helper.object_name}[custom_field_values][#{code}]" +        end + +        def form_input_options +          { +            input_html: {value: value, name: form_input_name}, +            label: name +          } +        end + +        def form_input +          @form_helper.input form_input_id, form_input_options +        end +      end      end      class Integer < Base        def value -        @raw_value.to_i +        @raw_value&.to_i        end        def validate          @valid = true -        unless @raw_value =~ /\A\d*\Z/ +        return if @raw_value.is_a?(Fixnum) || @raw_value.is_a?(Float) +        unless @raw_value.to_s =~ /\A\d*\Z/            @owner.errors.add errors_key, "'#{@raw_value}' is not a valid integer"            @valid = false          end        end      end +    class List < Integer +      def validate +        super +        return unless value.present? +        unless value >= 0 && value < options["list_values"].size +          @owner.errors.add errors_key, "'#{@raw_value}' is not a valid value" +          @valid = false +        end +      end + +      def display_value +        return unless value +        k = options["list_values"].is_a?(Hash) ? value.to_s : value.to_i +        options["list_values"][k] +      end + +      class Input < Base::Input +        def form_input_options +          collection = options["list_values"] +          collection = collection.each_with_index.to_a if collection.is_a?(Array) +          collection = collection.map(&:reverse) if collection.is_a?(Hash) +          super.update({ +            selected: value, +            collection: collection +          }) +        end +      end +    end + +    class Attachment < Base +      def initialize_custom_field +        custom_field_code = self.code +        _attr_name = attr_name +        _uploader_name = uploader_name +        owner.send :define_singleton_method, "read_uploader" do |attr| +          if attr.to_s == _attr_name +            custom_field_values[custom_field_code] +          else +            read_attribute attr +          end +        end + +        owner.send :define_singleton_method, "write_uploader" do |attr, val| +          if attr.to_s == _attr_name +            custom_field_values[custom_field_code] = val +          else +            write_attribute attr, val +          end +        end + +        owner.send :define_singleton_method, "#{_attr_name}_will_change!" do +          custom_field_values_will_change! +        end + +        _extension_whitelist = options["extension_whitelist"] + +        owner.send :define_singleton_method, "#{_uploader_name}_extension_whitelist" do +          _extension_whitelist +        end + +        unless owner.class.uploaders.has_key? _uploader_name.to_sym +          owner.class.mount_uploader _uploader_name, CustomFieldAttachmentUploader, mount_on: "custom_field_#{code}_raw_value" +        end +      end + +      def preprocess_value_for_assignment val +        if val.present? +          owner.send "#{uploader_name}=", val +        else +          @raw_value +        end +      end + +      def value +        owner.send "custom_field_#{code}" +      end + +      def raw_value +        @raw_value +      end + +      def attr_name +        "custom_field_#{code}_raw_value" +      end + +      def uploader_name +        "custom_field_#{code}" +      end + +      def display_value +        render_partial +      end + +      class Input < Base::Input +        def form_input_options +          super.update({ +            as: :file, +            wrapper: :horizontal_file_input +          }) +        end +      end +    end +      class String < Base        def value          "#{@raw_value}" diff --git a/app/models/export/base.rb b/app/models/export/base.rb index 6cf4c6b02..c65539635 100644 --- a/app/models/export/base.rb +++ b/app/models/export/base.rb @@ -1,4 +1,8 @@ +require 'net/http/post/multipart' +  class Export::Base < ActiveRecord::Base +  include Rails.application.routes.url_helpers +    self.table_name = "exports"    belongs_to :referential @@ -21,6 +25,22 @@ class Export::Base < ActiveRecord::Base      %w(zip csv json)    end +  def upload_file file +    url = URI.parse upload_workbench_export_url(self.workbench_id, self.id, host: Rails.application.config.rails_host) +    res = nil +    filename = File.basename(file.path) +    content_type = MIME::Types.type_for(filename).first&.content_type +    File.open(file.path) do |file_content| +      req = Net::HTTP::Post::Multipart.new url.path, +        file: UploadIO.new(file_content, content_type, filename), +        token: self.token_upload +      res = Net::HTTP.start(url.host, url.port) do |http| +        http.request(req) +      end +    end +    res +  end +    if Rails.env.development?      def self.force_load_descendants        path = Rails.root.join 'app/models/export' diff --git a/app/models/export/message.rb b/app/models/export/message.rb index b64b524ac..223429900 100644 --- a/app/models/export/message.rb +++ b/app/models/export/message.rb @@ -1,4 +1,4 @@ -class Export::Message < ActiveRecord::Base +class Export::Message < ApplicationModel    self.table_name = :export_messages    include IevInterfaces::Message diff --git a/app/models/export/resource.rb b/app/models/export/resource.rb index 98f103be4..2a63c14a8 100644 --- a/app/models/export/resource.rb +++ b/app/models/export/resource.rb @@ -1,4 +1,4 @@ -class Export::Resource < ActiveRecord::Base +class Export::Resource < ApplicationModel    self.table_name = :export_resources    include IevInterfaces::Resource diff --git a/app/models/export/simple_exporter/base.rb b/app/models/export/simple_exporter/base.rb index 4e6e8eba4..e77e23468 100644 --- a/app/models/export/simple_exporter/base.rb +++ b/app/models/export/simple_exporter/base.rb @@ -48,15 +48,15 @@ class Export::SimpleExporter::Base < Export::Base      exporter.export      set_status_from_exporter      convert_exporter_journal_to_messages -    self.file = tmp      self.save! +    upload_file tmp    end    def set_status_from_exporter      if exporter.status.to_s == "error"        self.status = :failed      elsif exporter.status.to_s == "success" -        self.status = :successful +      self.status = :successful      else        self.status = :warning      end diff --git a/app/models/import/base.rb b/app/models/import/base.rb index 62494c92e..82494b1dc 100644 --- a/app/models/import/base.rb +++ b/app/models/import/base.rb @@ -1,4 +1,4 @@ -class Import::Base < ActiveRecord::Base +class Import::Base < ApplicationModel    self.table_name = "imports"    validates :file, presence: true diff --git a/app/models/import/message.rb b/app/models/import/message.rb index c1900a718..30b76ec5c 100644 --- a/app/models/import/message.rb +++ b/app/models/import/message.rb @@ -1,4 +1,4 @@ -class Import::Message < ActiveRecord::Base +class Import::Message < ApplicationModel    self.table_name = :import_messages    include IevInterfaces::Message diff --git a/app/models/import/resource.rb b/app/models/import/resource.rb index 5bd011039..1951daacd 100644 --- a/app/models/import/resource.rb +++ b/app/models/import/resource.rb @@ -1,4 +1,4 @@ -class Import::Resource < ActiveRecord::Base +class Import::Resource < ApplicationModel    self.table_name = :import_resources    include IevInterfaces::Resource diff --git a/app/models/line_referential.rb b/app/models/line_referential.rb index 0d2ed39b1..08193c960 100644 --- a/app/models/line_referential.rb +++ b/app/models/line_referential.rb @@ -1,7 +1,7 @@ -class LineReferential < ActiveRecord::Base +class LineReferential < ApplicationModel    include ObjectidFormatterSupport    extend StifTransportModeEnumerations -   +    has_many :line_referential_memberships    has_many :organisations, through: :line_referential_memberships    has_many :lines, class_name: 'Chouette::Line' @@ -14,7 +14,7 @@ class LineReferential < ActiveRecord::Base    def add_member(organisation, options = {})      attributes = options.merge organisation: organisation -    line_referential_memberships.build attributes +    line_referential_memberships.build attributes unless organisations.include?(organisation)    end    validates :name, presence: true diff --git a/app/models/line_referential_membership.rb b/app/models/line_referential_membership.rb index b49d1b5b1..8371bdc32 100644 --- a/app/models/line_referential_membership.rb +++ b/app/models/line_referential_membership.rb @@ -1,4 +1,6 @@ -class LineReferentialMembership < ActiveRecord::Base +class LineReferentialMembership < ApplicationModel    belongs_to :organisation    belongs_to :line_referential + +  validates :organisation_id, presence: true, uniqueness: { scope: :line_referential }  end diff --git a/app/models/line_referential_sync.rb b/app/models/line_referential_sync.rb index 75c1e48a2..39e3846f0 100644 --- a/app/models/line_referential_sync.rb +++ b/app/models/line_referential_sync.rb @@ -1,4 +1,4 @@ -class LineReferentialSync < ActiveRecord::Base +class LineReferentialSync < ApplicationModel    include AASM    belongs_to :line_referential    has_many :line_referential_sync_messages, :dependent => :destroy diff --git a/app/models/line_referential_sync_message.rb b/app/models/line_referential_sync_message.rb index 3b6cf3367..00a2b58a3 100644 --- a/app/models/line_referential_sync_message.rb +++ b/app/models/line_referential_sync_message.rb @@ -1,4 +1,4 @@ -class LineReferentialSyncMessage < ActiveRecord::Base +class LineReferentialSyncMessage < ApplicationModel    belongs_to :line_referential_sync    enum criticity: [:info, :warning, :error] diff --git a/app/models/merge.rb b/app/models/merge.rb index e72c794fe..6e2a7036a 100644 --- a/app/models/merge.rb +++ b/app/models/merge.rb @@ -1,4 +1,4 @@ -class Merge < ActiveRecord::Base +class Merge < ApplicationModel    extend Enumerize    belongs_to :workbench @@ -138,7 +138,9 @@ class Merge < ActiveRecord::Base      new.switch do        referential_routes.each do |route|          existing_route = new.routes.find_by line_id: route.line_id, checksum: route.checksum -        unless existing_route +        if existing_route +          existing_route.merge_metadata_from route +        else            objectid = Chouette::Route.where(objectid: route.objectid).exists? ? nil : route.objectid            attributes = route.attributes.merge(              id: nil, @@ -196,7 +198,9 @@ class Merge < ActiveRecord::Base          existing_journey_pattern = new.journey_patterns.find_by route_id: existing_associated_route.id, checksum: journey_pattern.checksum -        unless existing_journey_pattern +        if existing_journey_pattern +          existing_journey_pattern.merge_metadata_from journey_pattern +        else            objectid = Chouette::JourneyPattern.where(objectid: journey_pattern.objectid).exists? ? nil : journey_pattern.objectid            attributes = journey_pattern.attributes.merge(              id: nil, @@ -241,7 +245,9 @@ class Merge < ActiveRecord::Base          existing_vehicle_journey = new.vehicle_journeys.find_by journey_pattern_id: existing_associated_journey_pattern.id, checksum: vehicle_journey.checksum -        unless existing_vehicle_journey +        if existing_vehicle_journey +          existing_vehicle_journey.merge_metadata_from vehicle_journey +        else            objectid = Chouette::VehicleJourney.where(objectid: vehicle_journey.objectid).exists? ? nil : vehicle_journey.objectid            attributes = vehicle_journey.attributes.merge(              id: nil, @@ -338,7 +344,9 @@ class Merge < ActiveRecord::Base            existing_time_table = line.time_tables.find_by checksum: candidate_time_table.checksum -          unless existing_time_table +          if existing_time_table +            existing_time_table.merge_metadata_from candidate_time_table +          else              objectid = Chouette::TimeTable.where(objectid: time_table.objectid).exists? ? nil : time_table.objectid              candidate_time_table.objectid = objectid diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 745bc0d22..5742c81e8 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -1,5 +1,5 @@  # coding: utf-8 -class Organisation < ActiveRecord::Base +class Organisation < ApplicationModel    include DataFormatEnumerations    has_many :users, :dependent => :destroy @@ -86,4 +86,8 @@ class Organisation < ActiveRecord::Base      workbenches.default    end +  def lines_scope +    functional_scope = sso_attributes.try(:[], "functional_scope") +    JSON.parse(functional_scope) if functional_scope +  end  end diff --git a/app/models/public_version.rb b/app/models/public_version.rb deleted file mode 100644 index 4dbf6ce27..000000000 --- a/app/models/public_version.rb +++ /dev/null @@ -1,4 +0,0 @@ -class PublicVersion < PaperTrail::Version -  # custom behaviour, e.g: -  self.table_name = :'public.versions' -end diff --git a/app/models/referential.rb b/app/models/referential.rb index 70148aa8e..1794126a2 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -1,5 +1,5 @@  # coding: utf-8 -class Referential < ActiveRecord::Base +class Referential < ApplicationModel    include DataFormatEnumerations    include ObjectidFormatterSupport @@ -241,7 +241,7 @@ class Referential < ActiveRecord::Base      end    end -  def self.new_from(from, functional_scope) +  def self.new_from(from, organisation)      Referential.new(        name: I18n.t("activerecord.copy", name: from.name),        slug: "#{from.slug}_clone", @@ -252,7 +252,7 @@ class Referential < ActiveRecord::Base        stop_area_referential: from.stop_area_referential,        created_from: from,        objectid_format: from.objectid_format, -      metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m, functional_scope) } +      metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m, organisation) }      )    end diff --git a/app/models/referential_cloning.rb b/app/models/referential_cloning.rb index d4b74bd52..f2c81009a 100644 --- a/app/models/referential_cloning.rb +++ b/app/models/referential_cloning.rb @@ -1,4 +1,4 @@ -class ReferentialCloning < ActiveRecord::Base +class ReferentialCloning < ApplicationModel    include AASM    belongs_to :source_referential, class_name: 'Referential'    belongs_to :target_referential, class_name: 'Referential' diff --git a/app/models/referential_metadata.rb b/app/models/referential_metadata.rb index 393dc70d3..7a8a01774 100644 --- a/app/models/referential_metadata.rb +++ b/app/models/referential_metadata.rb @@ -1,7 +1,7 @@  require 'activeattr_ext.rb'  require 'range_ext' -class ReferentialMetadata < ActiveRecord::Base +class ReferentialMetadata < ApplicationModel    belongs_to :referential, touch: true    belongs_to :referential_source, class_name: 'Referential'    has_array_of :lines, class_name: 'Chouette::Line' @@ -155,10 +155,10 @@ class ReferentialMetadata < ActiveRecord::Base    end    private :clear_periods -  def self.new_from(from, functional_scope) +  def self.new_from(from, organisation)      from.dup.tap do |metadata|        metadata.referential_source_id = from.referential_id -      metadata.line_ids = from.referential.lines.where(id: metadata.line_ids, objectid: functional_scope).collect(&:id) +      metadata.line_ids = from.referential.lines.where(id: metadata.line_ids).for_organisation(organisation).pluck(:id)        metadata.referential_id = nil      end    end diff --git a/app/models/referential_suite.rb b/app/models/referential_suite.rb index 4f825628c..f4a72f22c 100644 --- a/app/models/referential_suite.rb +++ b/app/models/referential_suite.rb @@ -1,4 +1,4 @@ -class ReferentialSuite < ActiveRecord::Base +class ReferentialSuite < ApplicationModel    belongs_to :new, class_name: 'Referential'    validate def validate_consistent_new      return true if new_id.nil? || new.nil? diff --git a/app/models/simple_exporter.rb b/app/models/simple_exporter.rb index c267b5b8c..1fcb76a29 100644 --- a/app/models/simple_exporter.rb +++ b/app/models/simple_exporter.rb @@ -64,7 +64,7 @@ class SimpleExporter < SimpleInterface    def map_item_to_rows item      return [item] unless configuration.item_to_rows_mapping -    configuration.item_to_rows_mapping.call(item).map {|row| row.is_a?(ActiveRecord::Base) ? row : CustomRow.new(row) } +    instance_exec(item, &configuration.item_to_rows_mapping).map {|row| row.is_a?(ActiveRecord::Base) ? row : CustomRow.new(row) }    end    def resolve_value item, col diff --git a/app/models/simple_interface.rb b/app/models/simple_interface.rb index 43c740b57..7b04a07df 100644 --- a/app/models/simple_interface.rb +++ b/app/models/simple_interface.rb @@ -1,4 +1,4 @@ -class SimpleInterface < ActiveRecord::Base +class SimpleInterface < ApplicationModel    attr_accessor :configuration, :interfaces_group    class << self diff --git a/app/models/stop_area_referential.rb b/app/models/stop_area_referential.rb index a9d3cc9b1..6c339547c 100644 --- a/app/models/stop_area_referential.rb +++ b/app/models/stop_area_referential.rb @@ -1,4 +1,4 @@ -class StopAreaReferential < ActiveRecord::Base +class StopAreaReferential < ApplicationModel    validates :registration_number_format, format: { with: /\AX*\z/ }    include ObjectidFormatterSupport @@ -12,7 +12,7 @@ class StopAreaReferential < ActiveRecord::Base    def add_member(organisation, options = {})      attributes = options.merge organisation: organisation -    stop_area_referential_memberships.build attributes +    stop_area_referential_memberships.build attributes unless organisations.include?(organisation)    end    def last_sync diff --git a/app/models/stop_area_referential_membership.rb b/app/models/stop_area_referential_membership.rb index 435970961..d507bc50e 100644 --- a/app/models/stop_area_referential_membership.rb +++ b/app/models/stop_area_referential_membership.rb @@ -1,4 +1,6 @@ -class StopAreaReferentialMembership < ActiveRecord::Base +class StopAreaReferentialMembership < ApplicationModel    belongs_to :organisation    belongs_to :stop_area_referential + +  validates :organisation_id, presence: true, uniqueness: { scope: :stop_area_referential }  end diff --git a/app/models/stop_area_referential_sync.rb b/app/models/stop_area_referential_sync.rb index e6cf2ecbc..8b48d35e6 100644 --- a/app/models/stop_area_referential_sync.rb +++ b/app/models/stop_area_referential_sync.rb @@ -1,4 +1,4 @@ -class StopAreaReferentialSync < ActiveRecord::Base +class StopAreaReferentialSync < ApplicationModel    include AASM    belongs_to :stop_area_referential    has_many :stop_area_referential_sync_messages, :dependent => :destroy diff --git a/app/models/stop_area_referential_sync_message.rb b/app/models/stop_area_referential_sync_message.rb index cd2e62405..642ccfc38 100644 --- a/app/models/stop_area_referential_sync_message.rb +++ b/app/models/stop_area_referential_sync_message.rb @@ -1,4 +1,4 @@ -class StopAreaReferentialSyncMessage < ActiveRecord::Base +class StopAreaReferentialSyncMessage < ApplicationModel    belongs_to :stop_area_referential_sync    enum criticity: [:info, :warning, :error] diff --git a/app/models/user.rb b/app/models/user.rb index eca7ede0c..29148d9e9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,4 @@ -class User < ActiveRecord::Base +class User < ApplicationModel    # Include default devise modules. Others available are:    # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable, :database_authenticatable diff --git a/app/models/workbench.rb b/app/models/workbench.rb index b5f4673bb..ef0b2eaa4 100644 --- a/app/models/workbench.rb +++ b/app/models/workbench.rb @@ -1,4 +1,4 @@ -class Workbench < ActiveRecord::Base +class Workbench < ApplicationModel    DEFAULT_WORKBENCH_NAME = "Gestion de l'offre"    include ObjectidFormatterSupport diff --git a/app/models/workgroup.rb b/app/models/workgroup.rb index 7e3e857ec..3e8409634 100644 --- a/app/models/workgroup.rb +++ b/app/models/workgroup.rb @@ -1,4 +1,4 @@ -class Workgroup < ActiveRecord::Base +class Workgroup < ApplicationModel    belongs_to :line_referential    belongs_to :stop_area_referential | 
