diff options
221 files changed, 2033 insertions, 456 deletions
@@ -33,9 +33,6 @@ gem 'spring', group: :development # ActiveRecord associations on top of PostgreSQL arrays gem 'has_array_of', af83: 'has_array_of' -# Track changes to your models' data. Good for auditing or versioning. -gem 'paper_trail' - gem 'rails-observers' # Use SeedBank for spliting seeds diff --git a/Gemfile.lock b/Gemfile.lock index 7fd58c713..4fb77eeb9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -341,10 +341,6 @@ GEM mini_portile2 (~> 2.3.0) open4 (1.3.4) orm_adapter (0.5.0) - paper_trail (4.1.0) - activerecord (>= 3.0, < 6.0) - activesupport (>= 3.0, < 6.0) - request_store (~> 1.1) parser (2.4.0.0) ast (~> 2.2) pg (0.20.0) @@ -657,7 +653,6 @@ DEPENDENCIES map_layers (= 0.0.4) mimemagic newrelic_rpm - paper_trail pg phantomjs poltergeist diff --git a/app/assets/stylesheets/components/_main_nav.sass b/app/assets/stylesheets/components/_main_nav.sass index 8e164fa01..ef3f14762 100644 --- a/app/assets/stylesheets/components/_main_nav.sass +++ b/app/assets/stylesheets/components/_main_nav.sass @@ -346,6 +346,7 @@ $menuW: 300px flex: 1 1 height: $menuH position: relative + margin-right: 2em h1 position: absolute @@ -355,8 +356,8 @@ $menuW: 300px line-height: 1.1 white-space: nowrap max-height: 1.1em - margin: 0 -1.4em 0 0 - padding: 0 1.4em 5px 0 + margin: 0 + padding: 0 0 5px 0 overflow: hidden text-overflow: ellipsis diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c4961123d..8b66e6097 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,5 @@ class ApplicationController < ActionController::Base - include PaperTrailSupport + include MetadataControllerSupport include Pundit include FeatureChecker @@ -10,7 +10,6 @@ class ApplicationController < ActionController::Base before_action :authenticate_user! before_action :set_locale - # Load helpers in rails engine helper LanguageEngine::Engine.helpers @@ -36,12 +35,6 @@ class ApplicationController < ActionController::Base end helper_method :current_organisation - def current_functional_scope - functional_scope = current_organisation.sso_attributes.try(:[], "functional_scope") if current_organisation - JSON.parse(functional_scope) if functional_scope - end - helper_method :current_functional_scope - def collection_name self.class.name.split("::").last.gsub('Controller', '').underscore end diff --git a/app/controllers/chouette_controller.rb b/app/controllers/chouette_controller.rb index 3e4f3af27..e6e7c0a8a 100644 --- a/app/controllers/chouette_controller.rb +++ b/app/controllers/chouette_controller.rb @@ -1,4 +1,3 @@ class ChouetteController < InheritedResources::Base - include PaperTrailSupport include ApplicationHelper end diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index 4afd12be1..a09cab783 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -38,12 +38,14 @@ class CompaniesController < ChouetteController protected def collection - @q = line_referential.companies.search(params[:q]) - + scope = line_referential.companies + @q = scope.search(params[:q]) + ids = @q.result(:distinct => true).pluck(:id) + scope = scope.where(id: ids) if sort_column && sort_direction - @companies ||= @q.result(:distinct => true).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page]) + @companies ||= scope.order(sort_column + ' ' + sort_direction).paginate(:page => params[:page]) else - @companies ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page]) + @companies ||= scope.order(:name).paginate(:page => params[:page]) end end @@ -69,7 +71,9 @@ class CompaniesController < ChouetteController end def company_params - params.require(:company).permit( :objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone ) + fields = [:objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone] + fields += permitted_custom_fields_params(Chouette::Company.custom_fields(line_referential.workgroup)) + params.require(:company).permit( fields ) end private diff --git a/app/controllers/compliance_control_blocks_controller.rb b/app/controllers/compliance_control_blocks_controller.rb index 9eee8dfaf..1173a548a 100644 --- a/app/controllers/compliance_control_blocks_controller.rb +++ b/app/controllers/compliance_control_blocks_controller.rb @@ -4,10 +4,23 @@ class ComplianceControlBlocksController < ChouetteController belongs_to :compliance_control_set actions :all, :except => [:show, :index] + after_action :display_errors, only: [:create, :update] + + def display_errors + unless @compliance_control_block.errors[:condition_attributes].empty? + flash[:error] = @compliance_control_block.errors[:condition_attributes].join(', ') + end + end + private def compliance_control_block_params params.require(:compliance_control_block).permit(:transport_mode, :transport_submode) end + protected + + alias_method :compliance_control_set, :parent + helper_method :compliance_control_set + end diff --git a/app/controllers/concerns/metadata_controller_support.rb b/app/controllers/concerns/metadata_controller_support.rb new file mode 100644 index 000000000..db83e79ae --- /dev/null +++ b/app/controllers/concerns/metadata_controller_support.rb @@ -0,0 +1,26 @@ +module MetadataControllerSupport + extend ActiveSupport::Concern + + included do + after_action :set_creator_metadata, only: :create + after_action :set_modifier_metadata, only: :update + end + + def user_for_metadata + current_user ? current_user.username : '' + end + + def set_creator_metadata + if resource.valid? + resource.try(:set_metadata!, :creator_username, user_for_metadata) + resource.try(:set_metadata!, :modifier_username, user_for_metadata) + end + end + + def set_modifier_metadata + _resource = @resources || [resource] + _resource.flatten.each do |r| + r.try :set_metadata!, :modifier_username, user_for_metadata + end + end +end diff --git a/app/controllers/concerns/paper_trail_support.rb b/app/controllers/concerns/paper_trail_support.rb deleted file mode 100644 index 4b0b1a7c7..000000000 --- a/app/controllers/concerns/paper_trail_support.rb +++ /dev/null @@ -1,11 +0,0 @@ -module PaperTrailSupport - extend ActiveSupport::Concern - - included do - before_action :set_paper_trail_whodunnit - - def user_for_paper_trail - current_user ? current_user.name : '' - end - end -end diff --git a/app/controllers/exports_controller.rb b/app/controllers/exports_controller.rb index a5282a514..c89da5000 100644 --- a/app/controllers/exports_controller.rb +++ b/app/controllers/exports_controller.rb @@ -3,13 +3,14 @@ class ExportsController < ChouetteController include RansackDateFilter include IevInterfaces skip_before_action :authenticate_user!, only: [:upload] + skip_before_action :verify_authenticity_token, only: [:upload] defaults resource_class: Export::Base, collection_name: 'exports', instance_name: 'export' def upload if params[:token] == resource.token_upload resource.file = params[:file] resource.save! - redirect_to [resource.workbench, resource] + render json: {status: :ok} else user_not_authorized end diff --git a/app/controllers/journey_patterns_collections_controller.rb b/app/controllers/journey_patterns_collections_controller.rb index da567779e..db92d48f3 100644 --- a/app/controllers/journey_patterns_collections_controller.rb +++ b/app/controllers/journey_patterns_collections_controller.rb @@ -74,6 +74,7 @@ class JourneyPatternsCollectionsController < ChouetteController def update state = JSON.parse request.raw_post Chouette::JourneyPattern.state_update route, state + @resources = route.journey_patterns errors = state.any? {|item| item['errors']} respond_to do |format| diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb index 806a70c8f..200e56a89 100644 --- a/app/controllers/referential_companies_controller.rb +++ b/app/controllers/referential_companies_controller.rb @@ -40,11 +40,12 @@ class ReferentialCompaniesController < ChouetteController end @q = scope.search(params[:q]) - + ids = @q.result(:distinct => true).pluck(:id) + scope = scope.where(id: ids) if sort_column && sort_direction - @companies ||= @q.result(:distinct => true).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page]) + @companies ||= scope.order(sort_column + ' ' + sort_direction).paginate(:page => params[:page]) else - @companies ||= @q.result(:distinct => true).order(:name).paginate(:page => params[:page]) + @companies ||= scope.order(:name).paginate(:page => params[:page]) end end @@ -57,7 +58,9 @@ class ReferentialCompaniesController < ChouetteController end def company_params - params.require(:company).permit( :objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone ) + fields = [:objectid, :object_version, :name, :short_name, :organizational_unit, :operating_department_name, :code, :phone, :fax, :email, :registration_number, :url, :time_zone] + fields += permitted_custom_fields_params(Chouette::Company.custom_fields(@referential.workgroup)) + params.require(:company).permit( fields ) end private diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 6e3694547..fe661651e 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -143,7 +143,7 @@ class ReferentialsController < ChouetteController def build_referential if params[:from] source_referential = Referential.find(params[:from]) - @referential = Referential.new_from(source_referential, current_functional_scope) + @referential = Referential.new_from(source_referential, current_organisation) end @referential.data_format = current_organisation.data_format diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb index 96a23c938..ac243c8eb 100644 --- a/app/controllers/routes_controller.rb +++ b/app/controllers/routes_controller.rb @@ -63,7 +63,8 @@ class RoutesController < ChouetteController end def duplicate - route = Chouette::Route.find(params[:id]).duplicate + source = Chouette::Route.find(params[:id]) + route = source.duplicate params[:opposite] flash[:notice] = t('routes.duplicate.success') redirect_to referential_line_path(@referential, route.line) end diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index d0d9f652d..b2634467d 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -161,7 +161,7 @@ class StopAreasController < ChouetteController helper_method :current_referential def stop_area_params - params.require(:stop_area).permit( + fields = [ :area_type, :children_ids, :city_name, @@ -192,7 +192,8 @@ class StopAreasController < ChouetteController :kind, :status, localized_names: Chouette::StopArea::AVAILABLE_LOCALIZATIONS - ) + ] + permitted_custom_fields_params(Chouette::StopArea.custom_fields) # XXX filter on the workgroup + params.require(:stop_area).permit(fields) end # Fake ransack filter diff --git a/app/decorators/route_decorator.rb b/app/decorators/route_decorator.rb index fa6367924..4a173cbb9 100644 --- a/app/decorators/route_decorator.rb +++ b/app/decorators/route_decorator.rb @@ -71,6 +71,23 @@ class RouteDecorator < AF83::Decorator end end + instance_decorator.action_link( + secondary: :show, + policy: :create_opposite, + if: ->{h.has_feature?(:create_opposite_routes) && object.opposite_route.nil?} + ) do |l| + l.content h.t('routes.create_opposite.title') + l.method :post + l.href do + h.duplicate_referential_line_route_path( + context[:referential], + context[:line], + object, + opposite: true + ) + end + end + instance_decorator.destroy_action_link do |l| l.data confirm: h.t('routes.actions.destroy_confirm') end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 479b661c8..702ca0ffc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -14,7 +14,7 @@ module ApplicationHelper def page_header_title(object) # Unwrap from decorator, we want to know the object model name object = object.object if object.try(:object) - + if Referential === object return object.full_name end @@ -36,8 +36,8 @@ module ApplicationHelper display = policy(object).synchronize? if policy(object).respond_to?(:synchronize?) rescue false if display info = t('last_update', time: l(object.updated_at, format: :short)) - if object.try(:versions) - author = object.versions.try(:last).try(:whodunnit) || t('default_whodunnit') + if object.has_metadata? + author = object.metadata.modifier_username || t('default_whodunnit') info = "#{info} <br/> #{t('whodunnit', author: author)}" end out += content_tag :div, info.html_safe, class: 'small last-update' @@ -136,5 +136,9 @@ module ApplicationHelper url_for(:controller => "/help", :action => "show") + '/' + target end - + def permitted_custom_fields_params custom_fields + [{ + custom_field_values: custom_fields.map(&:code) + }] + end end diff --git a/app/helpers/compliance_controls_helper.rb b/app/helpers/compliance_controls_helper.rb index 297ae3afa..abf909929 100644 --- a/app/helpers/compliance_controls_helper.rb +++ b/app/helpers/compliance_controls_helper.rb @@ -11,7 +11,7 @@ module ComplianceControlsHelper def compliance_control_metadatas(compliance_control) attributes = resource.class.dynamic_attributes - attributes.push(*resource.control_attributes.keys) if resource.respond_to? :control_attributes + attributes.push(*resource.control_attributes.keys) if resource&.control_attributes&.keys {}.tap do |hash| attributes.each do |attribute| @@ -19,4 +19,4 @@ module ComplianceControlsHelper end end end -end +end diff --git a/app/helpers/stop_areas_helper.rb b/app/helpers/stop_areas_helper.rb index 1c9d974a1..032f68494 100644 --- a/app/helpers/stop_areas_helper.rb +++ b/app/helpers/stop_areas_helper.rb @@ -68,7 +68,11 @@ module StopAreasHelper end def stop_area_registration_number_value stop_area - stop_area&.registration_number || stop_area&.stop_area_referential&.generate_registration_number + stop_area&.registration_number + end + + def stop_area_registration_number_hint + t "formtastic.hints.stop_area.registration_number" end def stop_area_status(stop_area) diff --git a/app/javascript/journey_patterns/actions/index.js b/app/javascript/journey_patterns/actions/index.js index 666157ea4..ea54f4e05 100644 --- a/app/javascript/journey_patterns/actions/index.js +++ b/app/javascript/journey_patterns/actions/index.js @@ -43,7 +43,7 @@ const actions = { }), updateCheckboxValue : (e, index) => ({ type : 'UPDATE_CHECKBOX_VALUE', - id : e.currentTarget.id, + position : e.currentTarget.id, index }), checkConfirmModal : (event, callback, stateChanged,dispatch) => { diff --git a/app/javascript/journey_patterns/components/CreateModal.js b/app/javascript/journey_patterns/components/CreateModal.js index 36b5740dc..946c13d9c 100644 --- a/app/javascript/journey_patterns/components/CreateModal.js +++ b/app/javascript/journey_patterns/components/CreateModal.js @@ -57,7 +57,7 @@ export default class CreateModal extends Component { <div className='row'> <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'> <div className='form-group'> - <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'published_name')}c</label> + <label className='control-label is-required'>{I18n.attribute_name('journey_pattern', 'published_name')}</label> <input type='text' ref='published_name' @@ -120,4 +120,4 @@ CreateModal.propTypes = { onOpenCreateModal: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired, onAddJourneyPattern: PropTypes.func.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js index d381b0d50..4eba80030 100644 --- a/app/javascript/journey_patterns/components/JourneyPattern.js +++ b/app/javascript/journey_patterns/components/JourneyPattern.js @@ -45,7 +45,7 @@ export default class JourneyPattern extends Component{ <input onChange = {(e) => this.props.onCheckboxChange(e)} type='checkbox' - id={sp.id} + id={sp.position} checked={sp.checked} disabled={(this.props.value.deletable || this.props.status.policy['journey_patterns.update'] == false || this.props.editMode == false) ? 'disabled' : ''} > diff --git a/app/javascript/journey_patterns/reducers/journeyPatterns.js b/app/javascript/journey_patterns/reducers/journeyPatterns.js index 6c38e9288..b046f2b38 100644 --- a/app/javascript/journey_patterns/reducers/journeyPatterns.js +++ b/app/javascript/journey_patterns/reducers/journeyPatterns.js @@ -22,7 +22,7 @@ const journeyPattern = (state = {}, action) =>{ } case 'UPDATE_CHECKBOX_VALUE': var updatedStopPoints = state.stop_points.map((s) => { - if (String(s.id) == action.id) { + if (String(s.position) == action.position) { return _.assign({}, s, {checked : !s.checked}) }else { return s diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js index 98b9eab4b..7c79dfe52 100644 --- a/app/javascript/time_tables/actions/index.js +++ b/app/javascript/time_tables/actions/index.js @@ -4,7 +4,6 @@ import reject from 'lodash/reject' import some from 'lodash/some' import every from 'lodash/every' import clone from '../../helpers/clone' -const I18n = clone(window, "I18n") const actions = { weekDays: (index) => { @@ -307,10 +306,11 @@ const actions = { }) }, errorModalKey: (periods, dayTypes) => { - const withoutPeriodsWithDaysTypes = reject(periods, 'deleted').length == 0 && some(dayTypes) && "withoutPeriodsWithDaysTypes" + // const withoutPeriodsWithDaysTypes = reject(periods, 'deleted').length == 0 && some(dayTypes) && "withoutPeriodsWithDaysTypes" const withPeriodsWithoutDayTypes = reject(periods, 'deleted').length > 0 && every(dayTypes, dt => dt == false) && "withPeriodsWithoutDayTypes" - return (withoutPeriodsWithDaysTypes || withPeriodsWithoutDayTypes) && (withoutPeriodsWithDaysTypes ? "withoutPeriodsWithDaysTypes" : "withPeriodsWithoutDayTypes") + // return (withoutPeriodsWithDaysTypes || withPeriodsWithoutDayTypes) && (withoutPeriodsWithDaysTypes ? "withoutPeriodsWithDaysTypes" : "withPeriodsWithoutDayTypes") + return withPeriodsWithoutDayTypes }, errorModalMessage: (errorKey) => { diff --git a/app/javascript/time_tables/components/ConfirmModal.js b/app/javascript/time_tables/components/ConfirmModal.js index 4e8583bc0..e4219348d 100644 --- a/app/javascript/time_tables/components/ConfirmModal.js +++ b/app/javascript/time_tables/components/ConfirmModal.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' -export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}, {I18n}) { +export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}) { return ( <div className={'modal fade ' + ((modal.type == 'confirm') ? 'in' : '')} id='ConfirmModal'> <div className='modal-container'> @@ -45,8 +45,4 @@ ConfirmModal.propTypes = { modal: PropTypes.object.isRequired, onModalAccept: PropTypes.func.isRequired, onModalCancel: PropTypes.func.isRequired -} - -ConfirmModal.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/ErrorModal.js b/app/javascript/time_tables/components/ErrorModal.js index 8af12f1d1..a512d28fd 100644 --- a/app/javascript/time_tables/components/ErrorModal.js +++ b/app/javascript/time_tables/components/ErrorModal.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import actions from '../actions' -export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { +export default function ErrorModal({dispatch, modal, onModalClose}) { return ( <div className={'modal fade ' + ((modal.type == 'error') ? 'in' : '')} id='ErrorModal'> <div className='modal-container'> @@ -37,8 +37,4 @@ export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { ErrorModal.propTypes = { modal: PropTypes.object.isRequired, onModalClose: PropTypes.func.isRequired -} - -ErrorModal.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js index 08a6e26fe..d9746a379 100644 --- a/app/javascript/time_tables/components/Metas.js +++ b/app/javascript/time_tables/components/Metas.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types' import actions from '../actions' import TagsSelect2 from './TagsSelect2' -export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}, {I18n}) { +export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}) { let colorList = ["", "#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"] return ( <div className='form-horizontal'> @@ -134,8 +134,4 @@ Metas.propTypes = { onUpdateColor: PropTypes.func.isRequired, onSelect2Tags: PropTypes.func.isRequired, onUnselect2Tags: PropTypes.func.isRequired -} - -Metas.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodForm.js b/app/javascript/time_tables/components/PeriodForm.js index d17a246f7..36ed6cfdf 100644 --- a/app/javascript/time_tables/components/PeriodForm.js +++ b/app/javascript/time_tables/components/PeriodForm.js @@ -33,7 +33,7 @@ const makeYearsOptions = (yearSelected) => { return arr } -export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}, {I18n}) { +export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}) { return ( <div className="container-fluid"> <div className="row"> @@ -143,8 +143,4 @@ PeriodForm.propTypes = { onUpdatePeriodForm: PropTypes.func.isRequired, onValidatePeriodForm: PropTypes.func.isRequired, timetable: PropTypes.object.isRequired -} - -PeriodForm.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodManager.js b/app/javascript/time_tables/components/PeriodManager.js index 6b817fe73..6871d0b9b 100644 --- a/app/javascript/time_tables/components/PeriodManager.js +++ b/app/javascript/time_tables/components/PeriodManager.js @@ -55,7 +55,7 @@ export default class PeriodManager extends Component { type='button' onClick={() => this.props.onOpenEditPeriodForm(this.props.value, this.props.index)} > - Modifier + {I18n.t('actions.edit')} </button> </li> <li className='delete-action'> @@ -64,7 +64,7 @@ export default class PeriodManager extends Component { onClick={() => this.props.onDeletePeriod(this.props.index, this.props.metas.day_types)} > <span className='fa fa-trash'></span> - Supprimer + {I18n.t('actions.destroy')} </button> </li> </ul> @@ -79,8 +79,4 @@ PeriodManager.propTypes = { currentDate: PropTypes.object.isRequired, onDeletePeriod: PropTypes.func.isRequired, onOpenEditPeriodForm: PropTypes.func.isRequired -} - -PeriodManager.contextTypes = { - I18n: PropTypes.object }
\ No newline at end of file diff --git a/app/javascript/time_tables/components/TagsSelect2.js b/app/javascript/time_tables/components/TagsSelect2.js index 43cf59fdf..dd8d6e9c0 100644 --- a/app/javascript/time_tables/components/TagsSelect2.js +++ b/app/javascript/time_tables/components/TagsSelect2.js @@ -40,7 +40,7 @@ export default class TagsSelect2 extends Component { allowClear: true, theme: 'bootstrap', width: '100%', - placeholder: this.context.I18n.t('time_tables.edit.select2.tag.placeholder'), + placeholder: I18n.t('time_tables.edit.select2.tag.placeholder'), ajax: { url: origin + path + '/tags.json', dataType: 'json', @@ -74,8 +74,4 @@ export default class TagsSelect2 extends Component { const formatRepo = (props) => { if(props.name) return props.name -} - -TagsSelect2.contextTypes = { - I18n: PropTypes.object }
\ No newline at end of file diff --git a/app/javascript/time_tables/components/Timetable.js b/app/javascript/time_tables/components/Timetable.js index 991f31435..3779fa2d0 100644 --- a/app/javascript/time_tables/components/Timetable.js +++ b/app/javascript/time_tables/components/Timetable.js @@ -31,11 +31,11 @@ export default class Timetable extends Component { <div className="table table-2entries mb-sm"> <div className="t2e-head w20"> <div className="th"> - <div className="strong">{this.context.I18n.t('time_tables.edit.synthesis')}</div> + <div className="strong">{I18n.t('time_tables.edit.synthesis')}</div> </div> - <div className="td"><span>{this.context.I18n.t('time_tables.edit.day_types')}</span></div> - <div className="td"><span>{this.context.I18n.t('time_tables.edit.periods')}</span></div> - <div className="td"><span>{this.context.I18n.t('time_tables.edit.exceptions')}</span></div> + <div className="td"><span>{I18n.t('time_tables.edit.day_types')}</span></div> + <div className="td"><span>{I18n.t('time_tables.edit.periods')}</span></div> + <div className="td"><span>{I18n.t('time_tables.edit.exceptions')}</span></div> </div> <div className="t2e-item-list w80"> <div> @@ -109,8 +109,4 @@ Timetable.propTypes = { onDeletePeriod: PropTypes.func.isRequired, onExcludeDateFromPeriod: PropTypes.func.isRequired, onIncludeDateInPeriod: PropTypes.func.isRequired -} - -Timetable.contextTypes = { - I18n: PropTypes.object -} +}
\ No newline at end of file diff --git a/app/javascript/time_tables/containers/App.js b/app/javascript/time_tables/containers/App.js index 5963f8f1d..c12e33ef5 100644 --- a/app/javascript/time_tables/containers/App.js +++ b/app/javascript/time_tables/containers/App.js @@ -10,7 +10,6 @@ import SaveTimetable from './SaveTimetable' import ConfirmModal from './ConfirmModal' import ErrorModal from './ErrorModal' import clone from '../../helpers/clone' -const I18n = clone(window, "I18n", true) class App extends Component { componentDidMount(){ diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index e00e9b1b0..537dcfc06 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -113,14 +113,9 @@ const actions = { type : 'EDIT_PURCHASE_WINDOWS_VEHICLEJOURNEY_MODAL', vehicleJourneys }), - selectPurchaseWindowsModal: (selectedWindow) =>({ + selectPurchaseWindowsModal: (selectedItem) =>({ type: 'SELECT_PURCHASE_WINDOW_MODAL', - selectedItem:{ - id: selectedWindow.id, - name: selectedWindow.name, - color: selectedWindow.color, - objectid: selectedWindow.objectid - } + selectedItem }), addSelectedPurchaseWindow: () => ({ type: 'ADD_SELECTED_PURCHASE_WINDOW' diff --git a/app/javascript/vehicle_journeys/components/Filters.js b/app/javascript/vehicle_journeys/components/Filters.js index ae3ab3476..93fe015a8 100644 --- a/app/javascript/vehicle_journeys/components/Filters.js +++ b/app/javascript/vehicle_journeys/components/Filters.js @@ -145,12 +145,12 @@ export default function Filters({filters, pagination, missions, onFilter, onRese <span className='btn btn-link' onClick={(e) => onResetFilters(e, pagination)}> - Effacer + {I18n.t('actions.erase')} </span> <span className='btn btn-default' onClick={(e) => onFilter(e, pagination)}> - Filtrer + {I18n.t('actions.filter')} </span> </div> </div> diff --git a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js b/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js index 90d72a801..827c36b76 100644 --- a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js +++ b/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js @@ -27,6 +27,19 @@ export default class CustomFieldsInputs extends Component { ) } + stringInput(cf){ + return( + <input + type='text' + ref={'custom_fields.' + cf.code} + className='form-control' + disabled={this.props.disabled} + defaultValue={cf.value} + onChange={(e) => this.props.onUpdate(cf.code, e.target.value) } + /> + ) + } + render() { return ( <div> diff --git a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js index 7a2686c13..9ee2e1849 100644 --- a/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js @@ -58,7 +58,7 @@ export default class TimetablesEditVehicleJourney extends Component { <div className='wrapper'> <div> <div className='form-group'> - <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_timetables'): I18n.t('vehicle_journeys.form.timetables')}</label> + <label className='control-label'>{this.props.modal.modalProps.timetables.length == 0 ? I18n.t('vehicle_journeys.vehicle_journeys_matrix.no_associated_timetables'): I18n.t('vehicle_journeys.form.time_tables')}</label> </div> </div> <div></div> diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js index b7e9691c1..60ad439b8 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/CompanySelect2.js @@ -31,7 +31,7 @@ export default class BSelect4 extends Component { theme: 'bootstrap', width: '100%', placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.affect_company'), - language: require('./fr'), + language: require('./language'), ajax: { url: origin + path + '/companies.json' + '?line_id=' + line, dataType: 'json', diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js index 96b34125d..cec39ab4e 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/MissionSelect2.js @@ -75,7 +75,7 @@ export default class BSelect4 extends Component { escapeMarkup: function (markup) { return markup; }, templateResult: formatRepo, placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.journey_pattern'), - language: require('./fr'), + language: require('./language'), allowClear: false, escapeMarkup: function (markup) { return markup; }, } diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js index 9a345b464..d5aad20d0 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/TimetableSelect2.js @@ -27,7 +27,7 @@ export default class BSelect4 extends Component { theme: 'bootstrap', width: '100%', placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.timetable'), - language: require('./fr'), + language: require('./language'), ajax: { url: origin + path + this.props.chunkURL, dataType: 'json', diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js index f5881cef7..50a941b6d 100644 --- a/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js +++ b/app/javascript/vehicle_journeys/components/tools/select2s/VJSelect2.js @@ -27,7 +27,7 @@ export default class BSelect4b extends Component { theme: 'bootstrap', placeholder: I18n.t('vehicle_journeys.vehicle_journeys_matrix.filters.id'), width: '100%', - language: require('./fr'), + language: require('./language'), ajax: { url: origin + path + '/vehicle_journeys.json', dataType: 'json', diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/fr.js b/app/javascript/vehicle_journeys/components/tools/select2s/fr.js deleted file mode 100644 index 20154d412..000000000 --- a/app/javascript/vehicle_journeys/components/tools/select2s/fr.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - errorLoading:function(){return"Les résultats ne peuvent pas être chargés."}, - inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n}, - inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n}, - loadingMore:function(){return"Chargement de résultats supplémentaires…"}, - maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t}, - noResults:function(){return"Aucun résultat trouvé"}, - searching:function(){return"Recherche en cours…"} -} diff --git a/app/javascript/vehicle_journeys/components/tools/select2s/language.js b/app/javascript/vehicle_journeys/components/tools/select2s/language.js new file mode 100644 index 000000000..9d587f96e --- /dev/null +++ b/app/javascript/vehicle_journeys/components/tools/select2s/language.js @@ -0,0 +1,9 @@ +module.exports = { + errorLoading: () => I18n.t('select2.error_loading'), + inputTooLong: (e) => I18n.t('select2.input_too_short', { count: e.input.length - e.maximum}), + inputTooShort: (e) => I18n.t('select2.input_too_long', { count: e.minimum - e.input.length }), + loadingMore: () => I18n.t('select2.loading_more'), + maximumSelected: (e) => I18n.t('select2.maximum_selected', {count: e.maximum}), + noResults: () => I18n.t('select2.no_results'), + searching: () => I18n.t('select2.searching') +} 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 diff --git a/app/policies/route_policy.rb b/app/policies/route_policy.rb index 0337a5300..4fcb6be11 100644 --- a/app/policies/route_policy.rb +++ b/app/policies/route_policy.rb @@ -20,4 +20,8 @@ class RoutePolicy < ApplicationPolicy def duplicate? create? end + + def create_opposite? + create? + end end diff --git a/app/services/route_way_cost_calculator.rb b/app/services/route_way_cost_calculator.rb index 2e30c94fc..d41a2e59a 100644 --- a/app/services/route_way_cost_calculator.rb +++ b/app/services/route_way_cost_calculator.rb @@ -5,7 +5,7 @@ class RouteWayCostCalculator def calculate! way_costs = StopAreasToWayCostsConverter.new(@route.stop_areas).convert - way_costs = TomTom.batch(way_costs) + way_costs = TomTom.matrix(way_costs) way_costs = WayCostCollectionJSONSerializer.dump(way_costs) @route.update(costs: way_costs) end diff --git a/app/uploaders/custom_field_attachment_uploader.rb b/app/uploaders/custom_field_attachment_uploader.rb new file mode 100644 index 000000000..411b65bc3 --- /dev/null +++ b/app/uploaders/custom_field_attachment_uploader.rb @@ -0,0 +1,12 @@ +class CustomFieldAttachmentUploader < CarrierWave::Uploader::Base + + storage :file + + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + def extension_whitelist + model.send "#{mounted_as}_extension_whitelist" + end +end diff --git a/app/views/autocomplete_purchase_windows/index.rabl b/app/views/autocomplete_purchase_windows/index.rabl index 1d0287602..bdc513c31 100644 --- a/app/views/autocomplete_purchase_windows/index.rabl +++ b/app/views/autocomplete_purchase_windows/index.rabl @@ -2,11 +2,12 @@ collection @purchase_windows, :object_root => false node do |window| { - :id => window.id, - :name => window.name, - :objectid => window.objectid, - :color => window.color, - :short_id => window.get_objectid.short_id, - :text => "<strong><span class='fa fa-circle' style='color:" + (window.color ? window.color : '#4b4b4b') + "'></span> " + window.name + " - " + window.get_objectid.short_id + "</strong>" + id: window.id, + name: window.name, + objectid: window.objectid, + color: window.color, + short_id: window.get_objectid.short_id, + bounding_dates: window.bounding_dates, + text: "<strong><span class='fa fa-circle' style='color:" + (window.color ? window.color : '#4b4b4b') + "'></span> " + window.name + " - " + window.get_objectid.short_id + "</strong>" } end diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim index 648c98928..880db99f6 100644 --- a/app/views/calendars/show.html.slim +++ b/app/views/calendars/show.html.slim @@ -6,8 +6,7 @@ .row .col-lg-6.col-md-6.col-sm-12.col-xs-12 = definition_list t('metadatas'), - { Calendar.tmf('short_name') => resource.try(:short_name), - Calendar.tmf('shared') => t("#{resource.shared}"), + { Calendar.tmf('shared') => t("#{resource.shared}"), Calendar.tmf('organisation') => resource.organisation.name, Calendar.tmf('dates') => resource.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe, Calendar.tmf('date_ranges') => resource.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe } @@ -18,7 +17,7 @@ .pagination.pull-right = @year .page_links - = link_to '', calendar_path(@calendar, year: (@year - 1)), class: 'previous_page' - = link_to '', calendar_path(@calendar, year: (@year + 1)), class: 'next_page' + = link_to '', workgroup_calendar_path(@workgroup, @calendar, year: (@year - 1)), class: 'previous_page' + = link_to '', workgroup_calendar_path(@workgroup, @calendar, year: (@year + 1)), class: 'next_page' = render 'time_tables/show_time_table', time_table: @calendar diff --git a/app/views/companies/_form.html.slim b/app/views/companies/_form.html.slim index 3979c5800..e8b3fcede 100644 --- a/app/views/companies/_form.html.slim +++ b/app/views/companies/_form.html.slim @@ -12,7 +12,9 @@ = f.input :time_zone, include_blank: true = f.input :url = f.input :registration_number, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@line_referential)}.company.registration_number")} - + - if resource.custom_fields(current_referential.workgroup).any? + - resource.custom_fields.each do |code, field| + = field.input(f).to_s .separator = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'company_form' diff --git a/app/views/companies/show.html.slim b/app/views/companies/show.html.slim index ca0a410b3..8960b92dd 100644 --- a/app/views/companies/show.html.slim +++ b/app/views/companies/show.html.slim @@ -6,8 +6,11 @@ .container-fluid .row .col-lg-6.col-md-6.col-sm-12.col-xs-12 - = definition_list t('metadatas'), - { 'ID Codif' => @company.try(:get_objectid).try(:short_id), - Chouette::Company.human_attribute_name(:phone) => resource.phone, - Chouette::Company.human_attribute_name(:email) => resource.email, - Chouette::Company.human_attribute_name(:url) => resource.url } + - attributes = { t('id_codif') => @company.try(:objectid).try(:local_id), + Chouette::Company.human_attribute_name(:phone) => @company.phone, + Chouette::Company.human_attribute_name(:email) => @company.email, + Chouette::Company.human_attribute_name(:url) => @company.url } + - @company.custom_fields(current_referential.workgroup).each do |code, field| + - attributes.merge!(field.name => field.display_value) + + = definition_list t('metadatas'), attributes diff --git a/app/views/compliance_control_blocks/edit.html.slim b/app/views/compliance_control_blocks/edit.html.slim index 49aee7705..1d32256b0 100644 --- a/app/views/compliance_control_blocks/edit.html.slim +++ b/app/views/compliance_control_blocks/edit.html.slim @@ -1,3 +1,4 @@ +- breadcrumb :compliance_control_set, compliance_control_set - page_header_content_for @compliance_control_block .page_content diff --git a/app/views/compliance_control_blocks/new.html.slim b/app/views/compliance_control_blocks/new.html.slim index 7d2551311..aab40572b 100644 --- a/app/views/compliance_control_blocks/new.html.slim +++ b/app/views/compliance_control_blocks/new.html.slim @@ -1,3 +1,5 @@ +- breadcrumb :compliance_control_set, compliance_control_set + .page_content .container-fluid .row diff --git a/app/views/lines/_filters.html.slim b/app/views/lines/_filters.html.slim index da0539bd0..f745d10a4 100644 --- a/app/views/lines/_filters.html.slim +++ b/app/views/lines/_filters.html.slim @@ -44,5 +44,5 @@ .actions - = link_to 'Effacer', @workbench, class: 'btn btn-link' - = f.submit 'Filtrer', class: 'btn btn-default' + = link_to t('actions.erase'), @workbench, class: 'btn btn-link' + = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/referential_companies/_form.html.slim b/app/views/referential_companies/_form.html.slim index b02eab3f1..0e7b20af4 100644 --- a/app/views/referential_companies/_form.html.slim +++ b/app/views/referential_companies/_form.html.slim @@ -1,18 +1,19 @@ -= semantic_form_for [@referential, @company] do |form| - = form.inputs do - = form.input :name, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.company.name") } - = form.input :short_name - = form.input :organizational_unit - = form.input :operating_department_name - = form.input :code - = form.input :phone, as: :phone - = form.input :fax, as: :phone - = form.input :email, as: :email - = form.input :time_zone, include_blank: true - = form.input :url - = form.input :registration_number, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.company.registration_number") } - = form.input :objectid, :required => !@company.new_record?, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.company.objectid") } - - = form.actions do - = form.action :submit, as: :button - = form.action :cancel, as: :link
\ No newline at end of file += simple_form_for [@referential, @company], html: {class: 'form-horizontal', id: 'company_form'}, wrapper: :horizontal_form do |f| + .row + .col-lg-12 + = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@line_referential)}.company.name")} + = f.input :short_name + = f.input :organizational_unit + = f.input :operating_department_name + = f.input :code + = f.input :phone + = f.input :fax + = f.input :email, as: :email + = f.input :time_zone, include_blank: true + = f.input :url + = f.input :registration_number, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@line_referential)}.company.registration_number")} + - if resource.custom_fields(@referential.workgroup).any? + - resource.custom_fields.each do |code, field| + = field.input(f).to_s + .separator + = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'company_form' diff --git a/app/views/referential_companies/edit.html.slim b/app/views/referential_companies/edit.html.slim index b3fcf6cd8..95be64aa1 100644 --- a/app/views/referential_companies/edit.html.slim +++ b/app/views/referential_companies/edit.html.slim @@ -1,3 +1,5 @@ - breadcrumb :referential_company, @referential, @company - page_header_content_for @company -= render 'form' +.page_content + .container-fluid + = render 'form' diff --git a/app/views/referential_companies/new.html.slim b/app/views/referential_companies/new.html.slim index 5e59db139..1dfdc8eb5 100644 --- a/app/views/referential_companies/new.html.slim +++ b/app/views/referential_companies/new.html.slim @@ -1,2 +1,6 @@ - breadcrumb :referential_companies, @referential -= render 'form' +.page_content + .container-fluid + .row + .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 + = render 'form' diff --git a/app/views/referential_companies/show.html.slim b/app/views/referential_companies/show.html.slim index 1599145be..8ad011edf 100644 --- a/app/views/referential_companies/show.html.slim +++ b/app/views/referential_companies/show.html.slim @@ -17,8 +17,11 @@ .container-fluid .row .col-lg-6.col-md-6.col-sm-12.col-xs-12 - = definition_list t('metadatas'), - { t('id_codif') => @company.try(:objectid).try(:local_id), - Chouette::Company.human_attribute_name(:phone) => @company.phone, - Chouette::Company.human_attribute_name(:email) => @company.email, - Chouette::Company.human_attribute_name(:url) => @company.url } + - attributes = { t('id_codif') => @company.try(:objectid).try(:local_id), + Chouette::Company.human_attribute_name(:phone) => @company.phone, + Chouette::Company.human_attribute_name(:email) => @company.email, + Chouette::Company.human_attribute_name(:url) => @company.url } + - @company.custom_fields(@referential.workgroup).each do |code, field| + - attributes.merge!(field.name => field.display_value) + + = definition_list t('metadatas'), attributes diff --git a/app/views/referential_lines/_filters.html.slim b/app/views/referential_lines/_filters.html.slim index 501f61c16..15db0e33e 100644 --- a/app/views/referential_lines/_filters.html.slim +++ b/app/views/referential_lines/_filters.html.slim @@ -1,7 +1,7 @@ = search_form_for @q, url: referential_line_path(@referential, @line), class: 'form form-filter' do |f| .ffg-row .input-group.search_bar class=filter_item_class(params[:q], :name_or_objectid_cont) - = f.search_field :name_or_objectid_cont, class: 'form-control', placeholder: "Indiquez un nom d'itinéraire ou un ID..." + = f.search_field :name_or_objectid_cont, class: 'form-control', placeholder: t('.name_or_objectid_cont') span.input-group-btn button.btn.btn-default#search-btn type='submit' span.fa.fa-search @@ -12,5 +12,5 @@ = f.input :wayback_eq_any, class: 'form-control', collection: Chouette::Route.wayback.values, as: :check_boxes, label: false, required: false, wrapper_html: { class: 'checkbox_list'}, label_method: lambda{|l| ("<span>" + t("enumerize.route.wayback.#{l}") + "</span>").html_safe} .actions - = link_to 'Effacer', referential_line_path(@referential, @line), class: 'btn btn-link' - = f.submit 'Filtrer', class: 'btn btn-default' + = link_to t('actions.erase'), referential_line_path(@referential, @line), class: 'btn btn-link' + = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim index f1fbdb5d8..f9fa4fcf7 100644 --- a/app/views/referential_vehicle_journeys/_filters.html.slim +++ b/app/views/referential_vehicle_journeys/_filters.html.slim @@ -68,5 +68,5 @@ .actions - = link_to 'Effacer', referential_vehicle_journeys_path(@referential), class: 'btn btn-link' - = f.submit 'Filtrer', class: 'btn btn-default' + = link_to t('actions.erase'), referential_vehicle_journeys_path(@referential), class: 'btn btn-link' + = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim index 96d847ec1..c378f871e 100644 --- a/app/views/referentials/_form.html.slim +++ b/app/views/referentials/_form.html.slim @@ -49,7 +49,7 @@ .separator .row .col-lg-11 - = subform.input :lines, as: :select, collection: Chouette::Line.includes(:company).order(:name).where(objectid: current_functional_scope), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': t('simple_form.labels.referential.placeholders.select_lines'), 'multiple': 'multiple', style: 'width: 100%' } + = subform.input :lines, as: :select, collection: Chouette::Line.includes(:company).order(:name).for_organisation(current_organisation), selected: subform.object.line_ids, label_method: :display_name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': t('simple_form.labels.referential.placeholders.select_lines'), 'multiple': 'multiple', style: 'width: 100%' } .col-lg-1 a.clear-lines.btn.btn-default href='#' .fa.fa-trash diff --git a/app/views/referentials/select_compliance_control_set.html.slim b/app/views/referentials/select_compliance_control_set.html.slim index 69c87aab2..7be82364d 100644 --- a/app/views/referentials/select_compliance_control_set.html.slim +++ b/app/views/referentials/select_compliance_control_set.html.slim @@ -1,3 +1,5 @@ +- breadcrumb @referential + .page_content .container-fluid .row @@ -6,7 +8,7 @@ .row .col-lg-12 .form-group - = label_tag 'name', nil, class: 'string required col-sm-4 col-xs-5 control-label' + = label_tag ComplianceControlSet.ts, nil, class: 'string required col-sm-4 col-xs-5 control-label' .col-sm-8.col-xs-7 = select_tag :compliance_control_set, options_from_collection_for_select(@compliance_control_sets, "id", "name"), class: 'select optional form-control' .separator diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim index d2e750fb0..d4571c173 100644 --- a/app/views/routes/show.html.slim +++ b/app/views/routes/show.html.slim @@ -34,7 +34,7 @@ end \ ), \ TableBuilderHelper::Column.new( \ - key: :deleted_at, \ + name: Chouette::Line.tmf('activated'), \ attribute: Proc.new { |s| line_status(s.try(:stop_area).deleted_at) } \ ), \ TableBuilderHelper::Column.new( \ diff --git a/app/views/shared/custom_fields/_attachment.html.slim b/app/views/shared/custom_fields/_attachment.html.slim new file mode 100644 index 000000000..32d0fda4d --- /dev/null +++ b/app/views/shared/custom_fields/_attachment.html.slim @@ -0,0 +1,4 @@ +- if field.value.present? + = link_to I18n.t("custom_fields.#{field.owner.class.name.demodulize.underscore}.#{field.code}.link"), field.value.url +- else + = "-" diff --git a/app/views/shared/iev_interfaces/_messages.html.slim b/app/views/shared/iev_interfaces/_messages.html.slim index 022f4ee01..14157a88d 100644 --- a/app/views/shared/iev_interfaces/_messages.html.slim +++ b/app/views/shared/iev_interfaces/_messages.html.slim @@ -9,6 +9,6 @@ - else .col-md-6= export_message_content message .col-md-6 - - if message.criticity != "info" + - if message.resource_attributes pre = JSON.pretty_generate message.resource_attributes || {} diff --git a/app/views/stop_areas/_filters.html.slim b/app/views/stop_areas/_filters.html.slim index a32638567..c698eaaa5 100644 --- a/app/views/stop_areas/_filters.html.slim +++ b/app/views/stop_areas/_filters.html.slim @@ -41,5 +41,5 @@ input_html: { checked: @status.try(:[], :deactivated) } .actions - = link_to 'Effacer', @workbench, class: 'btn btn-link' - = f.submit 'Filtrer', class: 'btn btn-default' + = link_to t('actions.erase'), @workbench, class: 'btn btn-link' + = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim index 1cba88f94..00f2ad8bb 100644 --- a/app/views/stop_areas/_form.html.slim +++ b/app/views/stop_areas/_form.html.slim @@ -48,7 +48,7 @@ - if has_feature?(:stop_area_waiting_time) = f.input :waiting_time, input_html: { min: 0 } - = f.input :registration_number, required: stop_area_registration_number_is_required(f.object), :input_html => {title: stop_area_registration_number_title(f.object), value: stop_area_registration_number_value(f.object)} + = f.input :registration_number, required: stop_area_registration_number_is_required(f.object), :input_html => {title: stop_area_registration_number_title(f.object), value: stop_area_registration_number_value(f.object)}, hint: stop_area_registration_number_hint = f.input :fare_code = f.input :nearest_topic_name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.nearest_topic_name")} = f.input :comment, as: :text, :input_html => {:rows => 5, :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.comment")} @@ -62,6 +62,12 @@ = f.input :stairs_availability, as: :select, :collection => [[t("true"), true], [t("false"), false]], :include_blank => true = f.input :lift_availability, as: :select, :collection => [[t("true"), true], [t("false"), false]], :include_blank => true + - if resource.custom_fields(resource.stop_area_referential.workgroup).any? + .custom_fields + h3 = t("stop_areas.stop_area.custom_fields") + - resource.custom_fields(resource.stop_area_referential.workgroup).each do |code, field| + = field.input(f).to_s + .separator = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'stop_area_form' diff --git a/app/views/stop_areas/index.html.slim b/app/views/stop_areas/index.html.slim index 587efbdaa..fbdb54e02 100644 --- a/app/views/stop_areas/index.html.slim +++ b/app/views/stop_areas/index.html.slim @@ -32,7 +32,7 @@ attribute: 'registration_number' \ ), \ TableBuilderHelper::Column.new( \ - name: t('activerecord.attributes.stop_area.state'), \ + name: Chouette::StopArea.tmf('state'), \ attribute: Proc.new { |s| stop_area_status(s) } \ ), \ TableBuilderHelper::Column.new( \ diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim index a6147b86d..851bd9b82 100644 --- a/app/views/stop_areas/show.html.slim +++ b/app/views/stop_areas/show.html.slim @@ -23,4 +23,6 @@ t('activerecord.attributes.stop_area.state') => stop_area_status(@stop_area), @stop_area.human_attribute_name(:comment) => @stop_area.try(:comment), }) + - @stop_area.custom_fields.each do |code, field| + - attributes.merge!(field.name => field.display_value) = definition_list t('metadatas'), attributes diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index cd55d3ce6..7fcee545f 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -7,7 +7,7 @@ - if has_feature? :purchase_windows = link_to I18n.t("purchase_windows.index.title"), [@referential, :purchase_windows], class: 'btn btn-primary sticky-action', target: :blank - if @route.opposite_route.present? - = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary sticky-action') + = link_to(t('routes.actions.reversed_vehicle_journey'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary sticky-action') .page_content @@ -33,7 +33,6 @@ | window.all_missions = #{(@all_missions.to_json).html_safe}; | window.custom_fields = #{(@custom_fields.to_json).html_safe}; | window.extra_headers = #{(@extra_headers.to_json).html_safe}; - // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; - if has_feature?(:vehicle_journeys_return_route) = javascript_tag do diff --git a/app/views/workbench_outputs/show.html.slim b/app/views/workbench_outputs/show.html.slim index a9e106dbb..b310119e6 100644 --- a/app/views/workbench_outputs/show.html.slim +++ b/app/views/workbench_outputs/show.html.slim @@ -6,7 +6,8 @@ .row.mb-sm .col-lg-12.text-right = link_to t('.see_current_output'), referential_path(@workbench.output.current), class: 'btn btn-primary' if @workbench.output&.current - = link_to t('merges.actions.create'), new_workbench_merge_path(@workbench), class: 'btn btn-primary' + - if policy(Merge).create? + = link_to t('merges.actions.create'), new_workbench_merge_path(@workbench), class: 'btn btn-primary' .page_content .container-fluid diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index 8312338d0..7dd1583fa 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -32,7 +32,8 @@ end \ ), \ TableBuilderHelper::Column.new( \ - key: :status, \ + key: :archived_at, \ + name: Referential.tmf('status'), \ attribute: Proc.new {|w| w.referential_read_only? ? ("<div class='td-block'><span class='fa fa-archive'></span><span>#{t('activerecord.attributes.referential.archived_at')}</span></div>").html_safe : ("<div class='td-block'><span class='sb sb-lg sb-preparing'></span><span>#{t('activerecord.attributes.referential.archived_at_null')}</span></div>").html_safe} \ ), \ TableBuilderHelper::Column.new( \ @@ -45,7 +46,7 @@ ), \ TableBuilderHelper::Column.new( \ key: :lines, \ - name: t('activerecord.attributes.referential.number_of_lines'), \ + name: Referential.tmf('number_of_lines'), \ attribute: Proc.new {|w| w.lines.count} \ ), \ TableBuilderHelper::Column.new( \ diff --git a/config/database/ci.yml b/config/database/ci.yml index 44103454a..5671cb6ad 100644 --- a/config/database/ci.yml +++ b/config/database/ci.yml @@ -1,4 +1,4 @@ -test: +test: &default adapter: <%= ENV.fetch 'RAILS_DB_ADAPTER', 'postgis' %> encoding: unicode pool: <%= ENV.fetch 'RAILS_DB_POOLSIZE', '5' %> @@ -9,3 +9,7 @@ test: database: <%= ENV.fetch 'RAILS_DB_NAME', 'stif_boiv_test' %> username: <%= ENV['RAILS_DB_USER'] || ENV['POSTGRESQL_ENV_POSTGRES_USER'] || 'jenkins' %> password: <%= ENV['RAILS_DB_PASSWORD'] || ENV['POSTGRESQL_ENV_POSTGRES_PASSWORD'] %> + +# Only used to build assets +production: + <<: *default diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb index 4b9bd320d..8dbc3afee 100644 --- a/config/initializers/simple_form_bootstrap.rb +++ b/config/initializers/simple_form_bootstrap.rb @@ -72,9 +72,9 @@ SimpleForm.setup do |config| b.use :placeholder b.optional :maxlength b.optional :readonly - b.use :label, class: 'col-sm-3 control-label' + b.use :label, class: 'col-sm-4 col-xs-5 control-label' - b.wrapper tag: 'div', class: 'col-sm-9' do |ba| + b.wrapper tag: 'div', class: 'col-sm-8 col-xs-7' do |ba| ba.use :input ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' } ba.use :hint, wrap_with: { tag: 'p', class: 'help-block small' } diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml index 3d16e7c05..696ae2734 100644 --- a/config/locales/calendars.en.yml +++ b/config/locales/calendars.en.yml @@ -69,7 +69,7 @@ en: date_ranges: Date ranges dates: Dates shared: Shared - organisation: Organisation + organisation: Organization monday: "Monday" tuesday: "Tuesday" wednesday: "Wednesday" diff --git a/config/locales/companies.en.yml b/config/locales/companies.en.yml index becb087b1..f2b19bc19 100644 --- a/config/locales/companies.en.yml +++ b/config/locales/companies.en.yml @@ -16,7 +16,7 @@ en: index: title: "Companies" name: "Search by name..." - name_or_objectid: "Search by name or by Codifligne ID..." + name_or_objectid: "Search by name or by ID..." advanced_search: "Advanced search" activerecord: models: diff --git a/config/locales/compliance_check_sets.en.yml b/config/locales/compliance_check_sets.en.yml index 63708328b..73ecf8996 100644 --- a/config/locales/compliance_check_sets.en.yml +++ b/config/locales/compliance_check_sets.en.yml @@ -23,7 +23,7 @@ en: table_state: "%{lines_status} lines imported out of %{lines_in_compliance_check_set} in the archive" table_explanation: "These controls apply to all imported data and condition the construction of your organization's offer." table_title: Analysed lines state - metrics: "%{ok_count} ok, %{error_count} errors, %{warning_count} warnings, %{uncheck_count} n/a" + metrics: "%{error_count} errors, %{warning_count} warnings" metadatas: referential: "Object analysed" referential_type: "Apply to" diff --git a/config/locales/compliance_check_sets.fr.yml b/config/locales/compliance_check_sets.fr.yml index 20bf11d85..045fed4ce 100644 --- a/config/locales/compliance_check_sets.fr.yml +++ b/config/locales/compliance_check_sets.fr.yml @@ -19,7 +19,7 @@ fr: table_state: "%{lines_status} lignes valides sur %{lines_in_compliance_check_set} présentes dans l'offre de transport" table_explanation: Ces contrôles s’appliquent pour toutes les données importées et conditionnent la construction de l’offre de votre organisation table_title: État des lignes analysées - metrics: "%{ok_count} ok, %{error_count} errors, %{warning_count} warnings, %{uncheck_count} n/a" + metrics: "%{error_count} errors, %{warning_count} warnings" metadatas: referential: "Objet analysé" referential_type: "Appliqué à" diff --git a/config/locales/compliance_control_blocks.en.yml b/config/locales/compliance_control_blocks.en.yml index 275f05106..0ec979549 100644 --- a/config/locales/compliance_control_blocks.en.yml +++ b/config/locales/compliance_control_blocks.en.yml @@ -9,6 +9,12 @@ en: compliance_control_blocks: transport_mode: Transport mode sub_transport_mode: Transport submode + errors: + models: + compliance_control_block: + attributes: + condition_attributes: + taken: The same compliance control block already exists in this compliance control set compliance_control_blocks: clone: prefix: 'Copy of' @@ -16,8 +22,12 @@ en: destroy_confirm: Are you sure you want to destroy this block ? new: title: Create a control block + create: + title: Create a control block edit: title: "Edit the control block : %{name}" + update: + title: "Edit the control block : %{name}" metas: control: zero: "No controls" diff --git a/config/locales/compliance_control_blocks.fr.yml b/config/locales/compliance_control_blocks.fr.yml index a6720881f..5ce5b4729 100644 --- a/config/locales/compliance_control_blocks.fr.yml +++ b/config/locales/compliance_control_blocks.fr.yml @@ -9,6 +9,12 @@ fr: compliance_control_blocks: transport_mode: Mode de transport transport_submode: Sous-mode de transport + errors: + models: + compliance_control_block: + attributes: + condition_attributes: + taken: Un groupe de contrôle identique existe déjà au sein de ce jeu de contrôles compliance_control_blocks: clone: prefix: 'Copie de' @@ -16,8 +22,12 @@ fr: destroy_confirm: Etes vous sûr de supprimer ce bloc ? new: title: Créer un groupe de contrôle(s) + create: + title: Créer un groupe de contrôle(s) edit: title: "Editer le groupe de contrôle : %{name}" + update: + title: "Editer le groupe de contrôle : %{name}" metas: control: zero: "Aucun contrôle" diff --git a/config/locales/import_resources.en.yml b/config/locales/import_resources.en.yml index 386039319..4b1f9c394 100644 --- a/config/locales/import_resources.en.yml +++ b/config/locales/import_resources.en.yml @@ -6,7 +6,7 @@ en: table_state: "%{lines_imported} line(s) imported on %{lines_in_zipfile} presents in zipfile" table_title: "Satus of anlyzed files" table_explanation: "When calendriers.xml and/or commun.xml are not imported, then all lines file are not processed." - metrics: "%{ok_count} ok, %{error_count} errors, %{warning_count} warnings, %{uncheck_count} n/a" + metrics: "%{error_count} errors, %{warning_count} warnings" import_resources: <<: *resources activerecord: diff --git a/config/locales/import_resources.fr.yml b/config/locales/import_resources.fr.yml index 50fb7f1ca..93a576f01 100644 --- a/config/locales/import_resources.fr.yml +++ b/config/locales/import_resources.fr.yml @@ -6,7 +6,7 @@ fr: table_state: "%{lines_imported} ligne(s) importée(s) sur %{lines_in_zipfile} présente(s) dans l'archive" table_title: "Etat des fichiers analysés" table_explanation: "Dans le cas ou le(s) fichiers calendriers.xml et/ou commun.xml sont dans un état non importé, alors tous les fichiers lignes sont automatiquement dans un état non traité." - metrics: "%{ok_count} ok, %{error_count} errors, %{warning_count} warnings, %{uncheck_count} n/a" + metrics: "%{error_count} errors, %{warning_count} warnings" import_resources: <<: *resources activerecord: diff --git a/config/locales/line_referentials.en.yml b/config/locales/line_referentials.en.yml index 5663ed691..18ff28c24 100644 --- a/config/locales/line_referentials.en.yml +++ b/config/locales/line_referentials.en.yml @@ -7,7 +7,7 @@ en: edit: title: "Edit %{name} referential" show: - title: "iLICO synchronization" + title: Line referential synchronized: Synchronized status: Status message: Message diff --git a/config/locales/lines.en.yml b/config/locales/lines.en.yml index e61013725..1cd5150db 100644 --- a/config/locales/lines.en.yml +++ b/config/locales/lines.en.yml @@ -1,6 +1,5 @@ en: lines: &en_lines - search_no_results: "No line matching your query" actions: new: "Add a new line" edit: "Edit this line" @@ -20,6 +19,8 @@ en: show: 'Show' show_network: 'Show network' show_company: 'Show company' + filters: + name_or_objectid_cont: "Search by name or objectid" new: title: "Add a new line" create: @@ -33,11 +34,12 @@ en: routes: title: "Routes list" group_of_lines: "Groups of lines" + search_no_results: "No line matching your query" index: deactivated: "Disabled line" title: "Lines" line: "Line %{line}" - name_or_number_or_objectid: "Search by name, short name or ID Codif..." + name_or_number_or_objectid: "Search by name, short name or ID..." no_networks: "No networks" no_companies: "No companies" no_group_of_lines: "No group of lines" @@ -115,7 +117,7 @@ en: creator_id: "Created by" footnotes: "Footnotes" stable_id: External permanent idenifier" - state: State + state: Status activated: Activated deactivated: Deactivated formtastic: diff --git a/config/locales/lines.fr.yml b/config/locales/lines.fr.yml index d3069f1d1..058238710 100644 --- a/config/locales/lines.fr.yml +++ b/config/locales/lines.fr.yml @@ -1,6 +1,5 @@ fr: lines: &fr_lines - search_no_results: "Aucune ligne ne correspond à votre recherche" actions: new: "Ajouter une ligne" edit: "Editer cette ligne" @@ -20,6 +19,8 @@ fr: show: 'Consulter' show_network: 'Voir le réseau' show_company: 'Voir le transporteur principal' + filters: + name_or_objectid_cont: "Indiquez un nom d'itinéraire ou un ID..." new: title: "Ajouter une ligne" create: @@ -34,6 +35,7 @@ fr: title: "Liste des Itinéraires" itineraries: "Liste des séquences d'arrêts de la ligne" group_of_lines: "Groupes de lignes" + search_no_results: "Aucune ligne ne correspond à votre recherche" index: deactivated: "Ligne désactivée" title: "Lignes" diff --git a/config/locales/networks.en.yml b/config/locales/networks.en.yml index 94a8d9df0..2046a30ae 100644 --- a/config/locales/networks.en.yml +++ b/config/locales/networks.en.yml @@ -15,7 +15,7 @@ en: index: title: "Networks" name: "Search by name..." - name_or_objectid: "Search by name or by Codifligne ID..." + name_or_objectid: "Search by name or by ID..." advanced_search: "Advanced search" activerecord: models: diff --git a/config/locales/referentials.fr.yml b/config/locales/referentials.fr.yml index ec6c7c643..cf012ef8e 100644 --- a/config/locales/referentials.fr.yml +++ b/config/locales/referentials.fr.yml @@ -32,7 +32,7 @@ fr: title: 'Dupliquer un jeu de données' submit: "Valider" select_compliance_control_set: - title: "Sélection du jeu de contôles" + title: "Sélection du jeu de contrôles" actions: new: "Créer un jeu de données" destroy_confirm: "Etes vous sûr de vouloir supprimer ce jeu de données ?" diff --git a/config/locales/routes.en.yml b/config/locales/routes.en.yml index 58869b895..66805e050 100644 --- a/config/locales/routes.en.yml +++ b/config/locales/routes.en.yml @@ -15,7 +15,7 @@ en: export_hub_all: "Export HUB routes" add_stop_point: "Add stop point" new_stop_point: "Create new stop" - opposite_route_timetable: "Timetable back" + reversed_vehicle_journey: "Reversed vehicle journeys" new: title: "Add a new route" edit: diff --git a/config/locales/routes.fr.yml b/config/locales/routes.fr.yml index ddf706794..f4eefa10d 100644 --- a/config/locales/routes.fr.yml +++ b/config/locales/routes.fr.yml @@ -15,7 +15,8 @@ fr: export_hub_all: "Export HUB des itinéraires" add_stop_point: "Ajouter un arrêt" new_stop_point: "Créer un arrêt pour l'ajouter" - opposite_route_timetable: "Horaires retour" + reversed_vehicle_journey: "Horaires retour" + opposite: "%{name} (retour)" new: title: "Ajouter un itinéraire" edit: @@ -56,6 +57,9 @@ fr: stop_area_name: "Nom de l'arrêt" for_boarding: "Montée" for_alighting: "Descente" + create_opposite: + title: "Créer retour" + success: "itinéraire créé avec succès" duplicate: title: "Dupliquer l'itinéraire" success: "itinéraire dupliqué avec succès" diff --git a/config/locales/select2.en.yml b/config/locales/select2.en.yml new file mode 100644 index 000000000..308540af0 --- /dev/null +++ b/config/locales/select2.en.yml @@ -0,0 +1,15 @@ +en: + select2: + error_loading: The results cannot be loaded. + input_too_long: + one: Remove %{count} letter + other: Remove %{count} letters + input_too_short: + one: Type %{count} letter + other: Type %{count} letters + loading_more: Loading more… + maximum_selected: + one: You can select %{count} element + other: You can select %{count} elements + no_results: No Results + searching: Searching... diff --git a/config/locales/select2.fr.yml b/config/locales/select2.fr.yml new file mode 100644 index 000000000..4a37a3d78 --- /dev/null +++ b/config/locales/select2.fr.yml @@ -0,0 +1,15 @@ +fr: + select2: + error_loading: Les résultats ne peuvent pas être chargés. + input_too_long: + one: Supprimez %{count} caractère + other: Supprimez %{count} caractères + input_too_short: + one: Saisissez %{count} caractère + other: Saisissez %{count} caractères + loading_more: Chargement de résultats supplémentaires… + maximum_selected: + one: Vous pouvez sélectionner %{count} élément + other: Vous pouvez sélectionner %{count} élément + no_results: Aucun résultat trouvé + searching: Recherche en cours...
\ No newline at end of file diff --git a/config/locales/stop_area_referentials.en.yml b/config/locales/stop_area_referentials.en.yml index 11baf67e2..9d49d7c5d 100644 --- a/config/locales/stop_area_referentials.en.yml +++ b/config/locales/stop_area_referentials.en.yml @@ -4,7 +4,7 @@ en: sync: "Launch a new reflex synchronization" cancel_sync: "Cancel reflex synchronization" show: - title: 'Synchronization iCAR' + title: 'Stop area referential' activerecord: models: stop_area_referential: diff --git a/config/locales/stop_areas.en.yml b/config/locales/stop_areas.en.yml index 37d39b76c..1da4b58b4 100644 --- a/config/locales/stop_areas.en.yml +++ b/config/locales/stop_areas.en.yml @@ -8,7 +8,7 @@ en: registration_number: already_taken: Already taken cannot_be_empty: This field is mandatory - invalid: Incorrect value + invalid: "Incorrect value (expected value: \"%{mask}\")" default_geometry_success: "%{count} modified stop areas" stop_area: no_position: "No Position" @@ -18,6 +18,7 @@ en: general: "General" localisation: "Localisation" accessibility: "Accessibility" + custom_fields: "Custom fields" actions: new: "Add a new stop area" create: "Add a new stop area" @@ -47,9 +48,9 @@ en: export_hub_physical: "Export HUB physical" filters: name_or_objectid: "Search by name or by objectid..." - zip_code: Type a zip code... - city_name: Type a city name... - area_type: Type an area type... + zip_code: Enter a zip code... + city_name: Enter a city name... + area_type: Enter an area type... new: title: "Add a new stop" update: @@ -69,7 +70,7 @@ en: stop_managment: "Parent-child relations" access_managment: "Access Points and Links managment" access_points: "Access Points" - not_editable: "Le type d'arrêt est non modifiable" + not_editable: "The area type is not editable" state: active: Active deactivated: Deactivated @@ -147,7 +148,7 @@ en: zip_code: "Zip code" city_name: "City" waiting_time: Waiting time (minutes) - state: State + state: Status formtastic: titles: stop_area: @@ -205,6 +206,9 @@ en: comment: "Maximum length = 255." coordinates: "Coordinates are mandatory." projection_xy: "x,y in secondary referential, dot for decimal separator" + hints: + stop_area: + registration_number: Leave empty for automatic value. referential_stop_areas: <<: *en_stop_areas diff --git a/config/locales/stop_areas.fr.yml b/config/locales/stop_areas.fr.yml index aee112be7..6a5fbf24b 100644 --- a/config/locales/stop_areas.fr.yml +++ b/config/locales/stop_areas.fr.yml @@ -9,7 +9,7 @@ fr: registration_number: already_taken: Déjà utilisé cannot_be_empty: Ce champ est requis - invalid: Valeur invalide + invalid: "Valeur invalide (valeur attendue: \"%{mask}\")" default_geometry_success: "%{count} arrêts édités" stop_area: no_position: "Pas de position" @@ -19,6 +19,7 @@ fr: general: "General" localisation: "Localisation" accessibility: "Accessibilité" + custom_fields: "Champs personnalisés" actions: new: "Ajouter un arrêt" create: "Ajouter un arrêt" @@ -208,6 +209,9 @@ fr: comment: "Longueur maximale = 255." coordinates: "Les coordonnées sont obligatoires." projection_xy: "x,y dans le référentiel secondaire, le séparateur de décimales est 'point'" + hints: + stop_area: + registration_number: Laisser blanc pour assigner une valeur automatiquement. referential_stop_areas: <<: *fr_stop_areas diff --git a/config/locales/stop_points.en.yml b/config/locales/stop_points.en.yml index 72e138270..76e142ba1 100644 --- a/config/locales/stop_points.en.yml +++ b/config/locales/stop_points.en.yml @@ -43,8 +43,8 @@ en: created_at: Created updated_at: Updated deleted_at: "Activated" - for_boarding: "For boarding" - for_alighting: "For alighting" + for_boarding: "Boarding" + for_alighting: "Alighting" area_type: "Area type" city_name: "City name" zip_code: "Zip code" diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml index 79e805ee8..12d8d0da4 100644 --- a/config/locales/vehicle_journeys.en.yml +++ b/config/locales/vehicle_journeys.en.yml @@ -3,8 +3,8 @@ en: vehicle_journeys_matrix: filters: id: Filter by ID... - timetable: Filter by journey pattern... - timetable: Filter by timetable... + journey_pattern: Filter by journey pattern... + timetable: Filter by timetable... cancel_selection: "Cancel Selection" fetching_error: "There has been a problem fetching the data. Please reload the page to try again." line_routes: "Line's routes" diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml index 4336bc6dc..466eca684 100644 --- a/config/locales/vehicle_journeys.fr.yml +++ b/config/locales/vehicle_journeys.fr.yml @@ -4,7 +4,7 @@ fr: filters: id: Filtrer par ID course... journey_pattern: 'Filtrer par code, nom ou OID de mission...' - timetable: Filter by timetable... + timetable: Filtrer par calendrier... cancel_selection: "Annuler la sélection" fetching_error: "La récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème." line_routes: "Séquences d'arrêts de la ligne" @@ -121,7 +121,7 @@ fr: checksum: "Signature métier" comment: "Commentaires" company: "Transporteur" - company|_name: "Nom du transporteur" + company_name: "Nom du transporteur" created_at: "Créé le" creator_id: "Créé par" departure_time: "Départ" diff --git a/db/migrate/20150219175300_insert_default_organisation.rb b/db/migrate/20150219175300_insert_default_organisation.rb index 2734893f5..ac8ecb9b6 100644 --- a/db/migrate/20150219175300_insert_default_organisation.rb +++ b/db/migrate/20150219175300_insert_default_organisation.rb @@ -1,5 +1,5 @@ class InsertDefaultOrganisation < ActiveRecord::Migration - class Organisation < ActiveRecord::Base + class Organisation < ApplicationModel attr_accessor :name end diff --git a/db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb b/db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb new file mode 100644 index 000000000..e49be7e40 --- /dev/null +++ b/db/migrate/20180313082623_add_custom_field_values_to_stop_areas.rb @@ -0,0 +1,5 @@ +class AddCustomFieldValuesToStopAreas < ActiveRecord::Migration + def change + add_column :stop_areas, :custom_field_values, :jsonb + end +end diff --git a/db/migrate/20180316115003_add_custom_field_values_to_companies.rb b/db/migrate/20180316115003_add_custom_field_values_to_companies.rb new file mode 100644 index 000000000..1791a6970 --- /dev/null +++ b/db/migrate/20180316115003_add_custom_field_values_to_companies.rb @@ -0,0 +1,5 @@ +class AddCustomFieldValuesToCompanies < ActiveRecord::Migration + def change + add_column :companies, :custom_field_values, :json + end +end diff --git a/db/migrate/20180330074336_add_metadata_to_routes.rb b/db/migrate/20180330074336_add_metadata_to_routes.rb new file mode 100644 index 000000000..1a35dbb65 --- /dev/null +++ b/db/migrate/20180330074336_add_metadata_to_routes.rb @@ -0,0 +1,5 @@ +class AddMetadataToRoutes < ActiveRecord::Migration + def change + add_column :routes, :metadata, :jsonb + end +end diff --git a/db/migrate/20180330124436_add_metadata_to_other_models.rb b/db/migrate/20180330124436_add_metadata_to_other_models.rb new file mode 100644 index 000000000..db01c77df --- /dev/null +++ b/db/migrate/20180330124436_add_metadata_to_other_models.rb @@ -0,0 +1,28 @@ +class AddMetadataToOtherModels < ActiveRecord::Migration + def change + [ + Api::V1::ApiKey, + Calendar, + Chouette::AccessLink, + Chouette::AccessPoint, + Chouette::Company, + Chouette::ConnectionLink, + Chouette::GroupOfLine, + Chouette::JourneyPattern, + Chouette::Line, + Chouette::Network, + Chouette::PtLink, + Chouette::PurchaseWindow, + Chouette::RoutingConstraintZone, + Chouette::StopArea, + Chouette::StopPoint, + Chouette::TimeTable, + Chouette::Timeband, + Chouette::VehicleJourney, + ComplianceCheckSet, + ComplianceControlSet, + ].each do |model| + add_column model.table_name.split(".").last, :metadata, :jsonb, default: {} + end + end +end diff --git a/db/migrate/20180403065419_remove_papertrail_tables.rb b/db/migrate/20180403065419_remove_papertrail_tables.rb new file mode 100644 index 000000000..8494058e1 --- /dev/null +++ b/db/migrate/20180403065419_remove_papertrail_tables.rb @@ -0,0 +1,5 @@ +class RemovePapertrailTables < ActiveRecord::Migration + def change + drop_table :versions + end +end diff --git a/db/migrate/20180405133659_change_companies_custom_fields_values_type.rb b/db/migrate/20180405133659_change_companies_custom_fields_values_type.rb new file mode 100644 index 000000000..7248c29f6 --- /dev/null +++ b/db/migrate/20180405133659_change_companies_custom_fields_values_type.rb @@ -0,0 +1,8 @@ +class ChangeCompaniesCustomFieldsValuesType < ActiveRecord::Migration + def change + reversible do |dir| + dir.up { change_column :companies, :custom_field_values, 'jsonb USING CAST(custom_field_values AS jsonb)', :default => {} } + dir.down { change_column :companies, :custom_field_values, 'json USING CAST(custom_field_values AS json)', :default => {} } + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 77e35f449..7e0e9c2b5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -15,14 +15,14 @@ ActiveRecord::Schema.define(version: 20180319043333) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - enable_extension "hstore" enable_extension "postgis" + enable_extension "hstore" enable_extension "unaccent" create_table "access_links", id: :bigserial, force: :cascade do |t| t.integer "access_point_id", limit: 8 t.integer "stop_area_id", limit: 8 - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "comment" @@ -39,6 +39,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "link_orientation" t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "access_links", ["objectid"], name: "access_links_objectid_key", unique: true, using: :btree @@ -66,6 +67,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.text "import_xml" t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "access_points", ["objectid"], name: "access_points_objectid_key", unique: true, using: :btree @@ -77,6 +79,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.datetime "created_at" t.datetime "updated_at" t.integer "organisation_id", limit: 8 + t.jsonb "metadata", default: {} end add_index "api_keys", ["organisation_id"], name: "index_api_keys_on_organisation_id", using: :btree @@ -89,9 +92,10 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.integer "organisation_id", limit: 8 t.datetime "created_at" t.datetime "updated_at" + t.integer "workgroup_id", limit: 8 t.integer "int_day_types" t.date "excluded_dates", array: true - t.integer "workgroup_id", limit: 8 + t.jsonb "metadata", default: {} end add_index "calendars", ["organisation_id"], name: "index_calendars_on_organisation_id", using: :btree @@ -117,13 +121,12 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.datetime "updated_at" t.date "end_date" t.string "date_type" - t.string "mode" end add_index "clean_ups", ["referential_id"], name: "index_clean_ups_on_referential_id", using: :btree create_table "companies", id: :bigserial, force: :cascade do |t| - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "short_name" @@ -140,6 +143,8 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.text "import_xml" t.datetime "created_at" t.datetime "updated_at" + t.jsonb "custom_field_values" + t.jsonb "metadata", default: {} end add_index "companies", ["line_referential_id"], name: "index_companies_on_line_referential_id", using: :btree @@ -192,14 +197,15 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "status" t.integer "parent_id", limit: 8 t.string "parent_type" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "current_step_id" t.float "current_step_progress" t.string "name" t.datetime "started_at" t.datetime "ended_at" t.datetime "notified_parent_at" + t.jsonb "metadata", default: {} end add_index "compliance_check_sets", ["compliance_control_set_id"], name: "index_compliance_check_sets_on_compliance_control_set_id", using: :btree @@ -238,8 +244,9 @@ ActiveRecord::Schema.define(version: 20180319043333) do create_table "compliance_control_sets", id: :bigserial, force: :cascade do |t| t.string "name" t.integer "organisation_id", limit: 8 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.jsonb "metadata", default: {} end add_index "compliance_control_sets", ["organisation_id"], name: "index_compliance_control_sets_on_organisation_id", using: :btree @@ -265,7 +272,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do create_table "connection_links", id: :bigserial, force: :cascade do |t| t.integer "departure_id", limit: 8 t.integer "arrival_id", limit: 8 - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "comment" @@ -281,6 +288,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.integer "int_user_needs" t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "connection_links", ["objectid"], name: "connection_links_objectid_key", unique: true, using: :btree @@ -398,7 +406,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do end create_table "group_of_lines", id: :bigserial, force: :cascade do |t| - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "comment" @@ -407,6 +415,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.text "import_xml" t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "group_of_lines", ["line_referential_id"], name: "index_group_of_lines_on_line_referential_id", using: :btree @@ -460,9 +469,9 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "type" t.integer "parent_id", limit: 8 t.string "parent_type" + t.datetime "notified_parent_at" t.integer "current_step", default: 0 t.integer "total_steps", default: 0 - t.datetime "notified_parent_at" t.string "creator" end @@ -485,7 +494,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do create_table "journey_patterns", id: :bigserial, force: :cascade do |t| t.integer "route_id", limit: 8 - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "comment" @@ -499,6 +508,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.text "checksum_source" t.string "data_source_ref" t.json "costs" + t.jsonb "metadata", default: {} end add_index "journey_patterns", ["objectid"], name: "journey_patterns_objectid_key", unique: true, using: :btree @@ -572,6 +582,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.datetime "created_at" t.datetime "updated_at" t.boolean "seasonal" + t.jsonb "metadata", default: {} end add_index "lines", ["line_referential_id"], name: "index_lines_on_line_referential_id", using: :btree @@ -593,7 +604,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do add_index "merges", ["workbench_id"], name: "index_merges_on_workbench_id", using: :btree create_table "networks", id: :bigserial, force: :cascade do |t| - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.date "version_date" t.string "description" @@ -607,6 +618,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.integer "line_referential_id", limit: 8 t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "networks", ["line_referential_id"], name: "index_networks_on_line_referential_id", using: :btree @@ -631,13 +643,14 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.integer "start_of_link_id", limit: 8 t.integer "end_of_link_id", limit: 8 t.integer "route_id", limit: 8 - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "comment" t.decimal "link_distance", precision: 19, scale: 2 t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "pt_links", ["objectid"], name: "pt_links_objectid_key", unique: true, using: :btree @@ -645,13 +658,14 @@ ActiveRecord::Schema.define(version: 20180319043333) do create_table "purchase_windows", id: :bigserial, force: :cascade do |t| t.string "name" t.string "color" - t.daterange "date_ranges", array: true - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.daterange "date_ranges", array: true + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "objectid" t.string "checksum" t.text "checksum_source" t.integer "referential_id", limit: 8 + t.jsonb "metadata", default: {} end add_index "purchase_windows", ["referential_id"], name: "index_purchase_windows_on_referential_id", using: :btree @@ -742,6 +756,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.text "checksum_source" t.string "data_source_ref" t.json "costs" + t.jsonb "metadata" end add_index "routes", ["objectid"], name: "routes_objectid_key", unique: true, using: :btree @@ -750,13 +765,14 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "name" t.datetime "created_at" t.datetime "updated_at" - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.integer "route_id", limit: 8 - t.integer "stop_point_ids", limit: 8, array: true + t.integer "stop_point_ids", limit: 8, array: true t.string "checksum" t.text "checksum_source" t.string "data_source_ref" + t.jsonb "metadata", default: {} end create_table "routing_constraints_lines", id: false, force: :cascade do |t| @@ -810,7 +826,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do create_table "stop_areas", id: :bigserial, force: :cascade do |t| t.integer "parent_id", limit: 8 - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" t.string "comment" @@ -841,10 +857,9 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.integer "waiting_time" t.string "kind" t.jsonb "localized_names" - t.datetime "confirmed_at" - t.json "custom_field_values" - + t.jsonb "custom_field_values" + t.jsonb "metadata", default: {} end add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree @@ -860,13 +875,14 @@ ActiveRecord::Schema.define(version: 20180319043333) do create_table "stop_points", id: :bigserial, force: :cascade do |t| t.integer "route_id", limit: 8 t.integer "stop_area_id", limit: 8 - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.integer "position" t.string "for_boarding" t.string "for_alighting" t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end add_index "stop_points", ["objectid"], name: "stop_points_objectid_key", unique: true, using: :btree @@ -914,7 +930,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do add_index "time_table_periods", ["time_table_id"], name: "index_time_table_periods_on_time_table_id", using: :btree create_table "time_tables", id: :bigserial, force: :cascade do |t| - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8, default: 1 t.string "version" t.string "comment" @@ -929,6 +945,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "checksum" t.text "checksum_source" t.string "data_source_ref" + t.jsonb "metadata", default: {} end add_index "time_tables", ["calendar_id"], name: "index_time_tables_on_calendar_id", using: :btree @@ -944,13 +961,14 @@ ActiveRecord::Schema.define(version: 20180319043333) do add_index "time_tables_vehicle_journeys", ["vehicle_journey_id"], name: "index_time_tables_vehicle_journeys_on_vehicle_journey_id", using: :btree create_table "timebands", id: :bigserial, force: :cascade do |t| - t.string "objectid", null: false + t.string "objectid", null: false t.integer "object_version", limit: 8 t.string "name" - t.time "start_time", null: false - t.time "end_time", null: false + t.time "start_time", null: false + t.time "end_time", null: false t.datetime "created_at" t.datetime "updated_at" + t.jsonb "metadata", default: {} end create_table "users", id: :bigserial, force: :cascade do |t| @@ -1033,6 +1051,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.text "checksum_source" t.string "data_source_ref" t.jsonb "custom_field_values", default: {} + t.jsonb "metadata", default: {} end add_index "vehicle_journeys", ["objectid"], name: "vehicle_journeys_objectid_key", unique: true, using: :btree @@ -1074,8 +1093,10 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "name" t.integer "line_referential_id", limit: 8 t.integer "stop_area_referential_id", limit: 8 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "import_types", default: [], array: true + t.string "export_types", default: [], array: true end add_foreign_key "access_links", "access_points", name: "aclk_acpt_fkey" diff --git a/db/seeds/dev/custom_fields.seeds.rb b/db/seeds/dev/custom_fields.seeds.rb new file mode 100644 index 000000000..eb3afc394 --- /dev/null +++ b/db/seeds/dev/custom_fields.seeds.rb @@ -0,0 +1,52 @@ +# coding: utf-8 + +require_relative '../seed_helpers' + +Workgroup.find_each do |workgroup| + puts workgroup.inspect + + workgroup.custom_fields.seed_by(code: "capacity") do |field| + field.resource_type = "VehicleJourney" + field.name = "Bus Capacity" + field.field_type = "list" + field.options = { list_values: { "0": "", "1": "48 places", "2": "54 places" }} + end + + workgroup.custom_fields.seed_by(code: "company_commercial_name") do |field| + field.resource_type = "Company" + field.name = "Nom commercial" + field.field_type = "list" + field.options = { list_values: { "0": "", "1": "OuiBus", "2": "Alsa" }} + end + + workgroup.custom_fields.seed_by(code: "company_contact_name") do |field| + field.resource_type = "Company" + field.name = "Nom du référent" + field.field_type = "string" + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_list") do |field| + field.resource_type = "StopArea" + field.name = "Test de Liste" + field.field_type = "list" + field.options = { list_values: { "0": "", "1": "Valeur 1", "2": "Valeur 2" }} + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_string") do |field| + field.resource_type = "StopArea" + field.name = "Test de Texte" + field.field_type = "string" + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_integer") do |field| + field.resource_type = "StopArea" + field.name = "Test de Nomber" + field.field_type = "integer" + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_attachment") do |field| + field.resource_type = "StopArea" + field.name = "Test de Piece Jointe" + field.field_type = "attachment" + end +end diff --git a/db/seeds/development/custom_fields.seeds.rb b/db/seeds/development/custom_fields.seeds.rb new file mode 100644 index 000000000..eb3afc394 --- /dev/null +++ b/db/seeds/development/custom_fields.seeds.rb @@ -0,0 +1,52 @@ +# coding: utf-8 + +require_relative '../seed_helpers' + +Workgroup.find_each do |workgroup| + puts workgroup.inspect + + workgroup.custom_fields.seed_by(code: "capacity") do |field| + field.resource_type = "VehicleJourney" + field.name = "Bus Capacity" + field.field_type = "list" + field.options = { list_values: { "0": "", "1": "48 places", "2": "54 places" }} + end + + workgroup.custom_fields.seed_by(code: "company_commercial_name") do |field| + field.resource_type = "Company" + field.name = "Nom commercial" + field.field_type = "list" + field.options = { list_values: { "0": "", "1": "OuiBus", "2": "Alsa" }} + end + + workgroup.custom_fields.seed_by(code: "company_contact_name") do |field| + field.resource_type = "Company" + field.name = "Nom du référent" + field.field_type = "string" + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_list") do |field| + field.resource_type = "StopArea" + field.name = "Test de Liste" + field.field_type = "list" + field.options = { list_values: { "0": "", "1": "Valeur 1", "2": "Valeur 2" }} + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_string") do |field| + field.resource_type = "StopArea" + field.name = "Test de Texte" + field.field_type = "string" + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_integer") do |field| + field.resource_type = "StopArea" + field.name = "Test de Nomber" + field.field_type = "integer" + end + + workgroup.custom_fields.seed_by(code: "stop_area_test_attachment") do |field| + field.resource_type = "StopArea" + field.name = "Test de Piece Jointe" + field.field_type = "attachment" + end +end diff --git a/db/seeds/seed_helpers.rb b/db/seeds/seed_helpers.rb new file mode 100644 index 000000000..708362a6c --- /dev/null +++ b/db/seeds/seed_helpers.rb @@ -0,0 +1,12 @@ +class ActiveRecord::Base + def self.seed_by(key_attribute, &block) + model = find_or_initialize_by key_attribute + print "Seed #{name} #{key_attribute.inspect} " + yield model + + puts "[#{(model.changed? ? 'updated' : 'no change')}]" + model.save! + + model + end +end diff --git a/db/seeds/stif.seeds.rb b/db/seeds/stif.seeds.rb index aa87b6f6c..98192385f 100644 --- a/db/seeds/stif.seeds.rb +++ b/db/seeds/stif.seeds.rb @@ -1,46 +1,25 @@ # coding: utf-8 -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). -stop_area_referential = StopAreaReferential.find_or_create_by!(name: "Reflex", objectid_format: "stif_netex") -line_referential = LineReferential.find_or_create_by!(name: "CodifLigne", objectid_format: "stif_netex") +require_relative 'seed_helpers' -workgroup = Workgroup.find_or_create_by!(name: "Gestion de l'offre théorique IDFm") do |w| - w.line_referential = line_referential - w.stop_area_referential = stop_area_referential - w.export_types = ["Export::Netex"] +stif = Organisation.seed_by(code: "STIF") do |o| + o.name = 'STIF' end -Workbench.update_all workgroup_id: workgroup - -# Organisations -stif = Organisation.find_or_create_by!(code: "STIF") do |org| - org.name = 'STIF' +stop_area_referential = StopAreaReferential.seed_by(name: "Reflex") do |r| + r.objectid_format = "stif_netex" + r.add_member stif, owner: true end -# operator = Organisation.find_or_create_by!(code: 'transporteur-a') do |organisation| -# organisation.name = "Transporteur A" -# end - -# Member -line_referential.add_member stif, owner: true -# line_referential.add_member operator -stop_area_referential.add_member stif, owner: true -# stop_area_referential.add_member operator +line_referential = LineReferential.seed_by(name: "CodifLigne") do |r| + r.objectid_format = "stif_codifligne" + r.add_member stif, owner: true +end -# Users -# stif.users.find_or_create_by!(username: "admin") do |user| -# user.email = 'stif-boiv@af83.com' -# user.password = "secret" -# user.name = "STIF Administrateur" -# end -# -# operator.users.find_or_create_by!(username: "transporteur") do |user| -# user.email = 'stif-boiv+transporteur@af83.com' -# user.password = "secret" -# user.name = "Martin Lejeune" -# end +workgroup = Workgroup.seed_by(name: "Gestion de l'offre théorique IDFm") do |w| + w.line_referential = line_referential + w.stop_area_referential = stop_area_referential + w.export_types = ["Export::Netex"] +end -# Include all Lines in organisation functional_scope -stif.update sso_attributes: { functional_scope: line_referential.lines.pluck(:objectid) } -#operator.update sso_attributes: { functional_scope: line_referential.lines.limit(3).pluck(:objectid) } +Workbench.update_all workgroup_id: workgroup diff --git a/lib/route_way_cost_unit_converter.rb b/lib/route_way_cost_unit_converter.rb index 45edbf538..52515e52c 100644 --- a/lib/route_way_cost_unit_converter.rb +++ b/lib/route_way_cost_unit_converter.rb @@ -8,18 +8,24 @@ class RouteWayCostUnitConverter end end - private - # Round to 2 decimal places to appease JavaScript validation def self.meters_to_kilometers(num) return 0 unless num - (num / 1000.0).to_i + snap_to_one(num / 1000.0).to_i end def self.seconds_to_minutes(num) return 0 unless num - num / 60 + snap_to_one(num / 60.0).to_i + end + + private + + def self.snap_to_one(decimal) + return 1 if decimal > 0 && decimal <= 1 + + decimal end end diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake index 5b2c8ae3c..cb9ed77e7 100644 --- a/lib/tasks/ci.rake +++ b/lib/tasks/ci.rake @@ -10,6 +10,7 @@ namespace :ci do desc "Prepare CI build" task :setup do + cp "config/database.yml", "config/database.yml.orig" cp "config/database/ci.yml", "config/database.yml" puts "Use #{database_name} database" sh "RAILS_ENV=test rake db:drop db:create db:migrate" @@ -28,6 +29,7 @@ namespace :ci do end def deploy_env + return ENV["DEPLOY_ENV"] if ENV["DEPLOY_ENV"] if git_branch == "master" "dev" elsif git_branch.in?(deploy_envs) @@ -54,12 +56,12 @@ namespace :ci do desc "Deploy after CI" task :deploy do - return if ENV["CHOUETTE_DEPLOY_DISABLED"] - - if deploy_env - sh "cap #{deploy_env} deploy:migrations" - else - puts "No deploy for branch #{git_branch}" + unless ENV["CHOUETTE_DEPLOY_DISABLED"] + if deploy_env + sh "cap #{deploy_env} deploy:migrations deploy:seed" + else + puts "No deploy for branch #{git_branch}" + end end end @@ -75,6 +77,9 @@ namespace :ci do task :clean do puts "Drop #{database_name} database" sh "RAILS_ENV=test rake db:drop" + + # Restore projet config/database.yml + # cp "config/database.yml.orig", "config/database.yml" if File.exists?("config/database.yml.orig") end end diff --git a/lib/tom_tom.rb b/lib/tom_tom.rb index a1a2bda43..fcebcc7ac 100644 --- a/lib/tom_tom.rb +++ b/lib/tom_tom.rb @@ -19,4 +19,8 @@ module TomTom def self.batch(way_costs) TomTom::Batch.new(@connection).batch(way_costs) end + + def self.matrix(way_costs) + TomTom::Matrix.new(@connection).matrix(way_costs) + end end diff --git a/lib/tom_tom/matrix.rb b/lib/tom_tom/matrix.rb new file mode 100644 index 000000000..b0c8cc335 --- /dev/null +++ b/lib/tom_tom/matrix.rb @@ -0,0 +1,114 @@ +module TomTom + class Matrix + def initialize(connection) + @connection = connection + end + + def matrix(way_costs) + points_with_ids = points_from_way_costs(way_costs) + points = points_as_params(points_with_ids) + + Rails.logger.info "Invoke TomTom for #{points.size} points" + + response = @connection.post do |req| + req.url '/routing/1/matrix/json' + req.headers['Content-Type'] = 'application/json' + + req.params[:routeType] = 'shortest' + req.params[:travelMode] = 'bus' + + req.body = build_request_body(points) + end + + extract_costs_to_way_costs!( + way_costs, + points_with_ids, + JSON.parse(response.body) + ) + end + + def points_from_way_costs(way_costs) + points = [] + + way_costs.each do |way_cost| + departure_id, arrival_id = way_cost.id.split('-') + + departure = TomTom::Matrix::Point.new( + way_cost.departure, + departure_id + ) + arrival = TomTom::Matrix::Point.new( + way_cost.arrival, + arrival_id + ) + + # Don't add duplicate coordinates. This assumes that + # `way_costs` consists of an ordered route of points where + # each departure coordinate is the same as the preceding + # arrival coordinate. + if points.empty? || + points.last.coordinates != departure.coordinates + points << departure + end + + points << arrival + end + + points + end + + def points_as_params(points) + points.map do |point| + { + point: { + latitude: point.coordinates.lat, + longitude: point.coordinates.lng + } + } + end + end + + def build_request_body(points) + # Serialize `BigDecimal` values as floats to please the TomTom API + RequestJSONSerializer.dump({ + origins: points, + destinations: points + }) + end + + def extract_costs_to_way_costs!(way_costs, points, matrix_json) + way_costs = [] + + # `row` and `column` order is the same as `points` + matrix_json['matrix'].each_with_index do |row, row_i| + row.each_with_index do |column, column_i| + next if column['statusCode'] != 200 + + distance = column['response']['routeSummary']['lengthInMeters'] + + # Ignore costs between a point and itself (e.g. from A to A) + next if distance == 0 + + departure = points[row_i] + arrival = points[column_i] + + way_costs << WayCost.new( + departure: Geokit::LatLng.new( + departure.coordinates.lat, + departure.coordinates.lng + ), + arrival: Geokit::LatLng.new( + arrival.coordinates.lat, + arrival.coordinates.lng + ), + distance: distance, + time: column['response']['routeSummary']['travelTimeInSeconds'], + id: "#{departure.id}-#{arrival.id}" + ) + end + end + + way_costs + end + end +end diff --git a/lib/tom_tom/matrix/point.rb b/lib/tom_tom/matrix/point.rb new file mode 100644 index 000000000..435b4d4b0 --- /dev/null +++ b/lib/tom_tom/matrix/point.rb @@ -0,0 +1,18 @@ +module TomTom + class Matrix + class Point + attr_reader :coordinates, :id + + def initialize(coordinates, id) + @coordinates = coordinates + @id = id + end + + def ==(other) + other.is_a?(self.class) && + @coordinates == other.coordinates && + @id == other.id + end + end + end +end diff --git a/lib/tom_tom/matrix/request_json_serializer.rb b/lib/tom_tom/matrix/request_json_serializer.rb new file mode 100644 index 000000000..f4d12e482 --- /dev/null +++ b/lib/tom_tom/matrix/request_json_serializer.rb @@ -0,0 +1,25 @@ +module TomTom + class Matrix + class RequestJSONSerializer + def self.dump(hash) + hash[:origins].map! do |point| + point_to_f(point) + end + hash[:destinations].map! do |point| + point_to_f(point) + end + + JSON.dump(hash) + end + + private + + def self.point_to_f(point) + point[:point][:latitude] = point[:point][:latitude].to_f + point[:point][:longitude] = point[:point][:longitude].to_f + + point + end + end + end +end diff --git a/spec/controllers/exports_controller_spec.rb b/spec/controllers/exports_controller_spec.rb index 9d8dde4ff..e2b89fc26 100644 --- a/spec/controllers/exports_controller_spec.rb +++ b/spec/controllers/exports_controller_spec.rb @@ -85,7 +85,7 @@ RSpec.describe ExportsController, :type => :controller do context "with the token" do it 'should be successful' do post :upload, workbench_id: workbench.id, id: export.id, token: export.token_upload - expect(response).to be_redirect + expect(response).to be_success end end diff --git a/spec/controllers/routes_controller_spec.rb b/spec/controllers/routes_controller_spec.rb index e4dc6bc23..b7cb66b46 100644 --- a/spec/controllers/routes_controller_spec.rb +++ b/spec/controllers/routes_controller_spec.rb @@ -42,11 +42,14 @@ RSpec.describe RoutesController, type: :controller do before(:each) do post :create, line_id: route.line_id, referential_id: referential.id, - route: { name: "changed"} + route: { name: "changed", published_name: "published_name"} end it_behaves_like "line and referential linked" it_behaves_like "redirected to referential_line_path(referential,line)" + it "sets metadata" do + expect(Chouette::Route.last.metadata.creator_username).to eq @user.username + end end describe "PUT /update" do @@ -58,6 +61,9 @@ RSpec.describe RoutesController, type: :controller do it_behaves_like "route, line and referential linked" it_behaves_like "redirected to referential_line_path(referential,line)" + it "sets metadata" do + expect(Chouette::Route.last.metadata.modifier_username).to eq @user.username + end end describe "GET /show" do @@ -83,6 +89,42 @@ RSpec.describe RoutesController, type: :controller do expect(Chouette::Route.last.name).to eq(I18n.t('activerecord.copy', name: route.name)) expect(Chouette::Route.last.published_name).to eq(route.published_name) + expect(Chouette::Route.last.stop_area_ids).to eq route.stop_area_ids + end + + context "when opposite = true" do + it "creates a new route on the opposite way " do + expect do + post :duplicate, + referential_id: route.line.line_referential_id, + line_id: route.line_id, + id: route.id, + opposite: TRUE + end.to change { Chouette::Route.count }.by(1) + + expect(Chouette::Route.last.name).to eq(I18n.t('routes.opposite', name: route.name)) + expect(Chouette::Route.last.published_name).to eq(Chouette::Route.last.name) + expect(Chouette::Route.last.opposite_route).to eq(route) + expect(Chouette::Route.last.stop_area_ids).to eq route.stop_area_ids.reverse + end + end + + context "on a duplicated route" do + let!(:duplicated){ route.duplicate } + it "creates a new route on the opposite way " do + expect do + post :duplicate, + referential_id: duplicated.line.line_referential_id, + line_id: duplicated.line_id, + id: duplicated.id, + opposite: TRUE + end.to change { Chouette::Route.count }.by(1) + + expect(Chouette::Route.last.name).to eq(I18n.t('routes.opposite', name: duplicated.name)) + expect(Chouette::Route.last.published_name).to eq(Chouette::Route.last.name) + expect(Chouette::Route.last.opposite_route).to eq(duplicated) + expect(Chouette::Route.last.stop_area_ids).to eq duplicated.stop_area_ids.reverse + end end end end diff --git a/spec/factories/custom_fields.rb b/spec/factories/custom_fields.rb index 7c43a6147..db7a3823e 100644 --- a/spec/factories/custom_fields.rb +++ b/spec/factories/custom_fields.rb @@ -3,7 +3,7 @@ FactoryGirl.define do code "code" resource_type "VehicleJourney" sequence(:name){|n| "custom field ##{n}"} - field_type "list" + field_type "integer" options( { capacity: "0" } ) end end diff --git a/spec/features/calendars_permissions_spec.rb b/spec/features/calendars_permissions_spec.rb index 4857592d5..656c0dd78 100644 --- a/spec/features/calendars_permissions_spec.rb +++ b/spec/features/calendars_permissions_spec.rb @@ -1,8 +1,8 @@ RSpec.describe 'Calendars', type: :feature do login_user - let(:calendar) { create :calendar, organisation_id: 1 } - let(:workgroup) { calendar.workgroup } + let(:calendar) { create :calendar, organisation: first_organisation, workgroup: first_workgroup } + let(:workgroup) { first_workgroup } describe 'permissions' do before do diff --git a/spec/features/compliance_control_sets_spec.rb b/spec/features/compliance_control_sets_spec.rb index 0f4597db3..306f363a5 100644 --- a/spec/features/compliance_control_sets_spec.rb +++ b/spec/features/compliance_control_sets_spec.rb @@ -12,7 +12,7 @@ RSpec.describe "ComplianceControlSets", type: :feature do let(:other_control_cset) { create :compliance_control_set, organisation: other_orga } let(:blox){ - 2.times.map{ | _ | create :compliance_control_block, compliance_control_set: control_set } + 2.times.map{ |n| create :compliance_control_block, compliance_control_set: control_set, transport_mode: StifTransportModeEnumerations.transport_modes[n], transport_submode: StifTransportSubmodeEnumerations.transport_submodes[n] } } before do diff --git a/spec/fixtures/tom_tom_matrix.json b/spec/fixtures/tom_tom_matrix.json new file mode 100644 index 000000000..30048576c --- /dev/null +++ b/spec/fixtures/tom_tom_matrix.json @@ -0,0 +1,123 @@ +{ + "formatVersion": "0.0.1", + "matrix": [ + [ + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 0, + "travelTimeInSeconds": 0, + "trafficDelayInSeconds": 0, + "departureTime": "2018-03-23T11:20:17+01:00", + "arrivalTime": "2018-03-23T11:20:17+01:00" + } + } + }, + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 117947, + "travelTimeInSeconds": 8356, + "trafficDelayInSeconds": 0, + "departureTime": "2018-03-23T11:20:17+01:00", + "arrivalTime": "2018-03-23T13:39:32+01:00" + } + } + }, + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 999088, + "travelTimeInSeconds": 62653, + "trafficDelayInSeconds": 298, + "departureTime": "2018-03-23T11:20:17+01:00", + "arrivalTime": "2018-03-24T04:44:30+01:00" + } + } + } + ], + [ + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 117231, + "travelTimeInSeconds": 9729, + "trafficDelayInSeconds": 0, + "departureTime": "2018-03-23T11:20:17+01:00", + "arrivalTime": "2018-03-23T14:02:25+01:00" + } + } + }, + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 0, + "travelTimeInSeconds": 0, + "trafficDelayInSeconds": 0, + "departureTime": "2018-03-23T11:20:17+01:00", + "arrivalTime": "2018-03-23T11:20:17+01:00" + } + } + }, + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 1114635, + "travelTimeInSeconds": 72079, + "trafficDelayInSeconds": 298, + "departureTime": "2018-03-23T11:20:18+01:00", + "arrivalTime": "2018-03-24T07:21:36+01:00" + } + } + } + ], + [ + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 997232, + "travelTimeInSeconds": 63245, + "trafficDelayInSeconds": 179, + "departureTime": "2018-03-23T11:20:18+01:00", + "arrivalTime": "2018-03-24T04:54:23+01:00" + } + } + }, + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 1113108, + "travelTimeInSeconds": 68485, + "trafficDelayInSeconds": 52, + "departureTime": "2018-03-23T11:20:18+01:00", + "arrivalTime": "2018-03-24T06:21:43+01:00" + } + } + }, + { + "statusCode": 200, + "response": { + "routeSummary": { + "lengthInMeters": 344, + "travelTimeInSeconds": 109, + "trafficDelayInSeconds": 0, + "departureTime": "2018-03-23T11:20:18+01:00", + "arrivalTime": "2018-03-23T11:22:07+01:00" + } + } + } + ] + ], + "summary": { + "successfulRoutes": 9, + "totalRoutes": 9 + } +} diff --git a/spec/lib/compliance_control_set_cloner_spec.rb b/spec/lib/compliance_control_set_cloner_spec.rb index 7efe27659..0d3561e0e 100644 --- a/spec/lib/compliance_control_set_cloner_spec.rb +++ b/spec/lib/compliance_control_set_cloner_spec.rb @@ -49,7 +49,7 @@ RSpec.describe ComplianceControlSetCloner do context 'Directed Acyclic Graph is copied correctly' do let(:source_blox){ - 3.times.map{ |_| create :compliance_control_block, compliance_control_set: source_set } + 3.times.map{ |n| create :compliance_control_block, compliance_control_set: source_set, transport_mode: StifTransportModeEnumerations.transport_modes[n], transport_submode: StifTransportSubmodeEnumerations.transport_submodes[n] } } let(:direct_ccs){ 3.times.map{ |n| create :generic_attribute_control_min_max, compliance_control_set: source_set, name: "direct #{n.succ}", code: "direct-#{n.succ}" } diff --git a/spec/lib/compliance_control_set_copier_spec.rb b/spec/lib/compliance_control_set_copier_spec.rb index 0f15d86d0..d1a56cd7f 100644 --- a/spec/lib/compliance_control_set_copier_spec.rb +++ b/spec/lib/compliance_control_set_copier_spec.rb @@ -23,7 +23,7 @@ RSpec.describe ComplianceControlSetCopier do context 'Directed Acyclic Graph is copied correctly' do let(:cc_blox){ - 3.times.map{ |_| create :compliance_control_block, compliance_control_set: cc_set } + 3.times.map{ |n| create :compliance_control_block, compliance_control_set: cc_set, transport_mode: StifTransportModeEnumerations.transport_modes[n], transport_submode: StifTransportSubmodeEnumerations.transport_submodes[n] } } let!(:direct_ccs){ 3.times.map{ |n| create :compliance_control, compliance_control_set: cc_set, name: "direct #{n.succ}", code: "direct-#{n.succ}" } diff --git a/spec/lib/route_way_cost_unit_converter_spec.rb b/spec/lib/route_way_cost_unit_converter_spec.rb index 3c5e51710..aa25d57d2 100644 --- a/spec/lib/route_way_cost_unit_converter_spec.rb +++ b/spec/lib/route_way_cost_unit_converter_spec.rb @@ -35,4 +35,32 @@ RSpec.describe RouteWayCostUnitConverter do }) end end + + describe ".meters_to_kilometers" do + it "converts meters to integer kilometres" do + expect( + RouteWayCostUnitConverter.meters_to_kilometers(6350) + ).to eq(6) + end + + it "snaps values between 0 and 1 to 1" do + expect( + RouteWayCostUnitConverter.meters_to_kilometers(50) + ).to eq(1) + end + end + + describe ".seconds_to_minutes" do + it "converts seconds to minutes" do + expect( + RouteWayCostUnitConverter.seconds_to_minutes(300) + ).to eq(5) + end + + it "snaps values between 0 and 1 to 1" do + expect( + RouteWayCostUnitConverter.seconds_to_minutes(3) + ).to eq(1) + end + end end diff --git a/spec/lib/tom_tom/matrix/request_json_serializer_spec.rb b/spec/lib/tom_tom/matrix/request_json_serializer_spec.rb new file mode 100644 index 000000000..1fafad302 --- /dev/null +++ b/spec/lib/tom_tom/matrix/request_json_serializer_spec.rb @@ -0,0 +1,39 @@ +RSpec.describe TomTom::Matrix::RequestJSONSerializer do + describe ".dump" do + it "serializes BigDecimal values to floats" do + points = [{ + point: { + latitude: 52.50867.to_d, + longitude: 13.42879.to_d + }, + }] + data = { + origins: points, + destinations: points + } + + expect( + TomTom::Matrix::RequestJSONSerializer.dump(data) + ).to eq(<<-JSON.delete(" \n")) + { + "origins": [ + { + "point": { + "latitude": 52.50867, + "longitude": 13.42879 + } + } + ], + "destinations": [ + { + "point": { + "latitude": 52.50867, + "longitude": 13.42879 + } + } + ] + } + JSON + end + end +end diff --git a/spec/lib/tom_tom/matrix_spec.rb b/spec/lib/tom_tom/matrix_spec.rb new file mode 100644 index 000000000..605f1d254 --- /dev/null +++ b/spec/lib/tom_tom/matrix_spec.rb @@ -0,0 +1,228 @@ +RSpec.describe TomTom::Matrix do + let(:matrix) { TomTom::Matrix.new(nil) } + + describe "#points_from_way_costs" do + it "extracts a set of lat/lng coordinates from a list of WayCosts" do + way_costs = [ + WayCost.new( + departure: Geokit::LatLng.new(48.85086, 2.36143), + arrival: Geokit::LatLng.new(47.91231, 1.87606), + id: '44-77' + ), + WayCost.new( + departure: Geokit::LatLng.new(47.91231, 1.87606), + arrival: Geokit::LatLng.new(52.50867, 13.42879), + id: '77-88' + ) + ] + + expect( + matrix.points_from_way_costs(way_costs) + ).to eq([ + TomTom::Matrix::Point.new( + Geokit::LatLng.new(48.85086, 2.36143), + '44' + ), + TomTom::Matrix::Point.new( + Geokit::LatLng.new(47.91231, 1.87606), + '77' + ), + TomTom::Matrix::Point.new( + Geokit::LatLng.new(52.50867, 13.42879), + '88' + ) + ]) + end + end + + describe "#points_as_params" do + it "transforms a set of LatLng points into a hash for use by TomTom Matrix" do + points = [ + TomTom::Matrix::Point.new( + Geokit::LatLng.new(48.85086, 2.36143), + '44' + ), + TomTom::Matrix::Point.new( + Geokit::LatLng.new(47.91231, 1.87606), + '77' + ), + TomTom::Matrix::Point.new( + Geokit::LatLng.new(52.50867, 13.42879), + '88' + ) + ] + + expect( + matrix.points_as_params(points) + ).to eq([ + { + point: { + latitude: 48.85086, + longitude: 2.36143 + }, + }, + { + point: { + latitude: 47.91231, + longitude: 1.87606 + }, + }, + { + point: { + latitude: 52.50867, + longitude: 13.42879 + }, + } + ]) + end + end + + describe "#build_request_body" do + it "serializes BigDecimal coordinates to floats" do + points = [ + { + point: { + latitude: 48.85086.to_d, + longitude: 2.36143.to_d + }, + }, + { + point: { + latitude: 47.91231.to_d, + longitude: 1.87606.to_d + }, + }, + { + point: { + latitude: 52.50867.to_d, + longitude: 13.42879.to_d + }, + } + ] + + expect( + matrix.build_request_body(points) + ).to eq(<<-JSON.delete(" \n")) + { + "origins": [ + { + "point": { + "latitude": 48.85086, + "longitude": 2.36143 + } + }, + { + "point": { + "latitude": 47.91231, + "longitude": 1.87606 + } + }, + { + "point": { + "latitude": 52.50867, + "longitude": 13.42879 + } + } + ], + "destinations": [ + { + "point": { + "latitude": 48.85086, + "longitude": 2.36143 + } + }, + { + "point": { + "latitude": 47.91231, + "longitude": 1.87606 + } + }, + { + "point": { + "latitude": 52.50867, + "longitude": 13.42879 + } + } + ] + } + JSON + end + end + + describe "#extract_costs_to_way_costs!" do + it "puts distance & time costs in way_costs" do + way_costs = [ + WayCost.new( + departure: Geokit::LatLng.new(48.85086, 2.36143), + arrival: Geokit::LatLng.new(47.91231, 1.87606), + id: '55-99' + ), + WayCost.new( + departure: Geokit::LatLng.new(47.91231, 1.87606), + arrival: Geokit::LatLng.new(52.50867, 13.42879), + id: '99-22' + ) + ] + + expected_way_costs = [ + WayCost.new( + departure: Geokit::LatLng.new(48.85086, 2.36143), + arrival: Geokit::LatLng.new(47.91231, 1.87606), + distance: 117947, + time: 8356, + id: '55-99' + ), + WayCost.new( + departure: Geokit::LatLng.new(48.85086, 2.36143), + arrival: Geokit::LatLng.new(52.50867, 13.42879), + distance: 999088, + time: 62653, + id: '55-22' + ), + WayCost.new( + departure: Geokit::LatLng.new(47.91231, 1.87606), + arrival: Geokit::LatLng.new(48.85086, 2.36143), + distance: 117231, + time: 9729, + id: '99-55' + ), + WayCost.new( + departure: Geokit::LatLng.new(47.91231, 1.87606), + arrival: Geokit::LatLng.new(52.50867, 13.42879), + distance: 1114635, + time: 72079, + id: '99-22' + ), + WayCost.new( + departure: Geokit::LatLng.new(52.50867, 13.42879), + arrival: Geokit::LatLng.new(48.85086, 2.36143), + distance: 997232, + time: 63245, + id: '22-55' + ), + WayCost.new( + departure: Geokit::LatLng.new(52.50867, 13.42879), + arrival: Geokit::LatLng.new(47.91231, 1.87606), + distance: 1113108, + time: 68485, + id: '22-99' + ), + WayCost.new( + departure: Geokit::LatLng.new(52.50867, 13.42879), + arrival: Geokit::LatLng.new(52.50867, 13.42879), + distance: 344, + time: 109, + id: '22-22' + ) + ] + + matrix_response = JSON.parse(read_fixture('tom_tom_matrix.json')) + + points = matrix.points_from_way_costs(way_costs) + + expect( + matrix.extract_costs_to_way_costs!(way_costs, points, matrix_response) + ).to match_array(expected_way_costs) + end + end +end diff --git a/spec/models/api/v1/api_key_spec.rb b/spec/models/api/v1/api_key_spec.rb index cc483a118..5c5a6bde1 100644 --- a/spec/models/api/v1/api_key_spec.rb +++ b/spec/models/api/v1/api_key_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Api::V1::ApiKey, type: :model do subject { create(:api_key) } it { should validate_presence_of :organisation } - it { is_expected.to be_versioned } + it 'should have a valid factory' do expect(build(:api_key)).to be_valid diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb index 09ac0e416..e57eee3b2 100644 --- a/spec/models/calendar_spec.rb +++ b/spec/models/calendar_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Calendar, :type => :model do it { is_expected.to validate_presence_of(:organisation) } it { is_expected.to validate_presence_of(:name) } - it { is_expected.to be_versioned } + describe '#to_time_table' do let(:calendar) { create(:calendar, int_day_types: Calendar::MONDAY | Calendar::SUNDAY, date_ranges: [Date.today...(Date.today + 1.month)]) } diff --git a/spec/models/chouette/access_link_spec.rb b/spec/models/chouette/access_link_spec.rb index ced99eb1d..448c22d33 100644 --- a/spec/models/chouette/access_link_spec.rb +++ b/spec/models/chouette/access_link_spec.rb @@ -4,7 +4,7 @@ describe Chouette::AccessLink, :type => :model do subject { create(:access_link) } it { is_expected.to validate_uniqueness_of :objectid } - it { is_expected.to be_versioned } + describe '#get_objectid' do subject { super().get_objectid } diff --git a/spec/models/chouette/access_point_spec.rb b/spec/models/chouette/access_point_spec.rb index 2184c6ec2..9c637cf41 100644 --- a/spec/models/chouette/access_point_spec.rb +++ b/spec/models/chouette/access_point_spec.rb @@ -12,7 +12,7 @@ describe Chouette::AccessPoint, :type => :model do it { is_expected.to validate_presence_of :name } it { is_expected.to validate_numericality_of :latitude } it { is_expected.to validate_numericality_of :longitude } - it { is_expected.to be_versioned } + describe ".latitude" do it "should accept -90 value" do diff --git a/spec/models/chouette/company_spec.rb b/spec/models/chouette/company_spec.rb index 34b19eeda..677c60dd9 100644 --- a/spec/models/chouette/company_spec.rb +++ b/spec/models/chouette/company_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Chouette::Company, :type => :model do subject { create(:company) } it { should validate_presence_of :name } - it { is_expected.to be_versioned } + describe "#nullables empty" do it "should set null empty nullable attributes" do diff --git a/spec/models/chouette/connection_link_spec.rb b/spec/models/chouette/connection_link_spec.rb index 4ab67d007..4486e348c 100644 --- a/spec/models/chouette/connection_link_spec.rb +++ b/spec/models/chouette/connection_link_spec.rb @@ -8,7 +8,7 @@ describe Chouette::ConnectionLink, :type => :model do subject { create(:connection_link) } it { is_expected.to validate_uniqueness_of :objectid } - it { is_expected.to be_versioned } + describe '#get_objectid' do subject { super().get_objectid } diff --git a/spec/models/chouette/group_of_line_spec.rb b/spec/models/chouette/group_of_line_spec.rb index d43d75374..8b2df69e5 100644 --- a/spec/models/chouette/group_of_line_spec.rb +++ b/spec/models/chouette/group_of_line_spec.rb @@ -4,7 +4,7 @@ describe Chouette::GroupOfLine, :type => :model do subject { create(:group_of_line) } it { should validate_presence_of :name } - it { is_expected.to be_versioned } + describe "#stop_areas" do let!(:line){create(:line, :group_of_lines => [subject])} diff --git a/spec/models/chouette/journey_pattern_spec.rb b/spec/models/chouette/journey_pattern_spec.rb index dac45d6b5..078e3c1f1 100644 --- a/spec/models/chouette/journey_pattern_spec.rb +++ b/spec/models/chouette/journey_pattern_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Chouette::JourneyPattern, :type => :model do - it { is_expected.to be_versioned } + subject { create(:journey_pattern) } describe 'checksum' do diff --git a/spec/models/chouette/line_spec.rb b/spec/models/chouette/line_spec.rb index 056d5da9e..cd7cdcb09 100644 --- a/spec/models/chouette/line_spec.rb +++ b/spec/models/chouette/line_spec.rb @@ -7,7 +7,7 @@ describe Chouette::Line, :type => :model do # it { is_expected.to validate_presence_of :network } # it { is_expected.to validate_presence_of :company } it { should validate_presence_of :name } - it { is_expected.to be_versioned } + describe '#display_name' do it 'should display local_id, number, name and company name' do diff --git a/spec/models/chouette/network_spec.rb b/spec/models/chouette/network_spec.rb index 78a4150df..11ad7cacb 100644 --- a/spec/models/chouette/network_spec.rb +++ b/spec/models/chouette/network_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Chouette::Network, :type => :model do subject { create(:network) } it { should validate_presence_of :name } - it { is_expected.to be_versioned } + describe "#stop_areas" do let!(:line){create(:line, :network => subject)} diff --git a/spec/models/chouette/route/route_base_spec.rb b/spec/models/chouette/route/route_base_spec.rb index d24ad6348..3d4a87791 100644 --- a/spec/models/chouette/route/route_base_spec.rb +++ b/spec/models/chouette/route/route_base_spec.rb @@ -15,8 +15,7 @@ RSpec.describe Chouette::Route, :type => :model do #it { is_expected.to validate_presence_of :direction_code } it { is_expected.to validate_inclusion_of(:direction).in_array(%i(straight_forward backward clockwise counter_clockwise north north_west west south_west south south_east east north_east)) } it { is_expected.to validate_inclusion_of(:wayback).in_array(%i(outbound inbound)) } - it { is_expected.to be_versioned } - + context "reordering methods" do let(:bad_stop_point_ids){subject.stop_points.map { |sp| sp.id + 1}} let(:ident){subject.stop_points.map(&:id)} diff --git a/spec/models/chouette/route/route_duplication_spec.rb b/spec/models/chouette/route/route_duplication_spec.rb index 8b3a948a2..47233b04e 100644 --- a/spec/models/chouette/route/route_duplication_spec.rb +++ b/spec/models/chouette/route/route_duplication_spec.rb @@ -8,9 +8,6 @@ RSpec.describe Chouette::Route do route.duplicate expect( values_for_create(Chouette::Route.last, except: %w{objectid name checksum checksum_source}) ).to eq( values_for_create( route, except: %w{objectid name checksum checksum_source} ) ) end - it 'and others cannot' do - expect{ route.duplicate name: 'YAN', line_id: 42 }.to raise_error(ArgumentError) - end it 'same associated stop_areeas' do expect( route.duplicate.stop_areas.pluck(:id) ).to eq(route.stop_areas.pluck(:id)) end diff --git a/spec/models/chouette/routing_constraint_zone_spec.rb b/spec/models/chouette/routing_constraint_zone_spec.rb index bda6bb04a..321b41b7b 100644 --- a/spec/models/chouette/routing_constraint_zone_spec.rb +++ b/spec/models/chouette/routing_constraint_zone_spec.rb @@ -8,7 +8,7 @@ describe Chouette::RoutingConstraintZone, type: :model do it { is_expected.to validate_presence_of :route_id } # shoulda matcher to validate length of array ? xit { is_expected.to validate_length_of(:stop_point_ids).is_at_least(2) } - it { is_expected.to be_versioned } + describe 'checksum' do it_behaves_like 'checksum support' diff --git a/spec/models/chouette/stop_area_spec.rb b/spec/models/chouette/stop_area_spec.rb index e35300caf..185820388 100644 --- a/spec/models/chouette/stop_area_spec.rb +++ b/spec/models/chouette/stop_area_spec.rb @@ -13,7 +13,7 @@ describe Chouette::StopArea, :type => :model do it { should validate_presence_of :kind } it { should validate_numericality_of :latitude } it { should validate_numericality_of :longitude } - it { is_expected.to be_versioned } + describe "#area_type" do it "should validate the value is correct regarding to the kind" do diff --git a/spec/models/chouette/stop_point_spec.rb b/spec/models/chouette/stop_point_spec.rb index 6b9e7727f..ba3799619 100644 --- a/spec/models/chouette/stop_point_spec.rb +++ b/spec/models/chouette/stop_point_spec.rb @@ -4,7 +4,7 @@ describe Chouette::StopPoint, :type => :model do it { is_expected.to validate_uniqueness_of :objectid } it { is_expected.to validate_presence_of :stop_area } - it { is_expected.to be_versioned } + describe '#objectid' do subject { super().get_objectid } diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb index bb88877b9..a3354facb 100644 --- a/spec/models/chouette/time_table_spec.rb +++ b/spec/models/chouette/time_table_spec.rb @@ -6,7 +6,7 @@ describe Chouette::TimeTable, :type => :model do it { is_expected.to validate_presence_of :comment } it { is_expected.to validate_uniqueness_of :objectid } - it { is_expected.to be_versioned } + def create_time_table_periode time_table, start_date, end_date create(:time_table_period, time_table: time_table, :period_start => start_date, :period_end => end_date) diff --git a/spec/models/chouette/timeband_spec.rb b/spec/models/chouette/timeband_spec.rb index b960c203f..fa7c8f06e 100644 --- a/spec/models/chouette/timeband_spec.rb +++ b/spec/models/chouette/timeband_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Chouette::Timeband, :type => :model do - it { is_expected.to be_versioned } + describe '#create' do context 'when valid' do diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index c69655bd4..6d44eeb2f 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Chouette::VehicleJourney, :type => :model do subject { create(:vehicle_journey) } - it { is_expected.to be_versioned } + it { should have_and_belong_to_many(:purchase_windows) } it "must be valid with an at-stop day offset of 1" do @@ -420,6 +420,7 @@ describe Chouette::VehicleJourney, :type => :model do state['published_journey_name'] = 'edited_name' state['published_journey_identifier'] = 'edited_identifier' state['custom_fields'] = {energy: {value: 99}} + create :custom_field, field_type: :integer, code: :energy, name: :energy Chouette::VehicleJourney.state_update(route, collection) expect(state['errors']).to be_nil @@ -867,7 +868,6 @@ describe Chouette::VehicleJourney, :type => :model do let!( :footnote_first) {create( :footnote, :code => "1", :label => "dummy 1", :line => route.line)} let!( :footnote_second) {create( :footnote, :code => "2", :label => "dummy 2", :line => route.line)} - it "should update vehicle's footnotes" do expect(Chouette::VehicleJourney.find(subject.id).footnotes).to be_empty subject.footnote_ids = [ footnote_first.id ] @@ -876,4 +876,54 @@ describe Chouette::VehicleJourney, :type => :model do end end end + + describe "#fill_passing_time_at_borders" do + before do + start = create :stop_area + border = create :stop_area, kind: :non_commercial, area_type: :border + border_2 = create :stop_area, kind: :non_commercial, area_type: :border + middle = create :stop_area + border_3 = create :stop_area, kind: :non_commercial, area_type: :border + border_4 = create :stop_area, kind: :non_commercial, area_type: :border + _end = create :stop_area + journey_pattern = create :journey_pattern + journey_pattern.stop_points.destroy_all + journey_pattern.stop_points << start_point = create(:stop_point, stop_area: start, position: 0) + journey_pattern.stop_points << border_point = create(:stop_point, stop_area: border, position: 1) + journey_pattern.stop_points << border_point_2 = create(:stop_point, stop_area: border_2, position: 2) + journey_pattern.stop_points << middle_point = create(:stop_point, stop_area: middle, position: 3) + journey_pattern.stop_points << border_point_3 = create(:stop_point, stop_area: border_3, position: 4) + journey_pattern.stop_points << border_point_4 = create(:stop_point, stop_area: border_4, position: 5) + journey_pattern.stop_points << end_point = create(:stop_point, stop_area: _end, position: 6) + journey_pattern.update_attribute :costs, { + "#{start_point.stop_area_id}-#{border_point.stop_area_id}" => {distance: 50}, + "#{border_point.stop_area_id}-#{border_point_2.stop_area_id}" => {distance: 0}, + "#{border_point_2.stop_area_id}-#{middle_point.stop_area_id}" => {distance: 100}, + "#{middle_point.stop_area_id}-#{border_point_3.stop_area_id}" => {distance: 100}, + "#{border_point_3.stop_area_id}-#{border_point_4.stop_area_id}" => {distance: 0}, + "#{border_point_4.stop_area_id}-#{end_point.stop_area_id}" => {distance: 100} + } + @journey = create :vehicle_journey, journey_pattern: journey_pattern + @journey.vehicle_journey_at_stops.destroy_all + @start = create :vehicle_journey_at_stop, stop_point: start_point, vehicle_journey: @journey + @target = create :vehicle_journey_at_stop, stop_point: border_point, vehicle_journey: @journey, arrival_time: nil, departure_time: nil + @target_2 = create :vehicle_journey_at_stop, stop_point: border_point_2, vehicle_journey: @journey, arrival_time: nil, departure_time: nil + @middle = create :vehicle_journey_at_stop, stop_point: middle_point, vehicle_journey: @journey, arrival_time: @start.arrival_time + 4.hours, departure_time: @start.departure_time + 4.hours + @target_3 = create :vehicle_journey_at_stop, stop_point: border_point_3, vehicle_journey: @journey, arrival_time: nil, departure_time: nil + @target_4 = create :vehicle_journey_at_stop, stop_point: border_point_4, vehicle_journey: @journey, arrival_time: nil, departure_time: nil + @end = create :vehicle_journey_at_stop, stop_point: end_point, vehicle_journey: @journey, arrival_time: @middle.arrival_time + 4.hours, departure_time: @middle.departure_time + 4.hours + end + + it "should compute passing time" do + @journey.reload.fill_passing_time_at_borders + expect(@target.reload.arrival_time.to_i).to eq (@start.reload.departure_time + 1.0/3 * (@middle.reload.arrival_time - @start.departure_time)).to_i + expect(@target_2.reload.arrival_time).to eq @target.arrival_time + expect(@target.departure_time).to eq @target.arrival_time + expect(@target_2.departure_time).to eq @target.arrival_time + expect(@target_3.reload.arrival_time.to_i).to eq (@middle.reload.departure_time + 0.5 * (@end.reload.arrival_time - @middle.departure_time)).to_i + expect(@target_4.reload.arrival_time).to eq @target_3.arrival_time + expect(@target_3.departure_time).to eq @target_3.arrival_time + expect(@target_4.departure_time).to eq @target_3.arrival_time + end + end end diff --git a/spec/models/compliance_check_set_spec.rb b/spec/models/compliance_check_set_spec.rb index 61421287a..b6f854829 100644 --- a/spec/models/compliance_check_set_spec.rb +++ b/spec/models/compliance_check_set_spec.rb @@ -12,7 +12,7 @@ RSpec.describe ComplianceCheckSet, type: :model do it { should have_many :compliance_checks } it { should have_many :compliance_check_blocks } - it { is_expected.to be_versioned } + describe "#update_status" do it "updates :status to successful when all resources are OK" do diff --git a/spec/models/compliance_control_block_spec.rb b/spec/models/compliance_control_block_spec.rb index 4abe0ed9c..089d78434 100644 --- a/spec/models/compliance_control_block_spec.rb +++ b/spec/models/compliance_control_block_spec.rb @@ -17,4 +17,16 @@ RSpec.describe ComplianceControlBlock, type: :model do it { should_not allow_values( *%w{ demandResponseBus nightus irportLinkBus highrequencyBus expressBUs Shuttle suburban regioalRail interregion4lRail }) .for(:transport_submode) } + + context "transport mode & submode uniqueness" do + let(:cc_block) {create :compliance_control_block, transport_mode: 'bus', transport_submode: 'nightBus'} + let(:cc_set1) { cc_block.compliance_control_set } + let(:cc_set2) { create :compliance_control_set } + + it "sould be unique in a compliance control set" do + expect( ComplianceControlBlock.new(transport_mode: 'bus', transport_submode: 'nightBus', compliance_control_set: cc_set1) ).not_to be_valid + expect( ComplianceControlBlock.new(transport_mode: 'bus', transport_submode: 'nightBus', compliance_control_set: cc_set2) ).to be_valid + end + +end end diff --git a/spec/models/compliance_control_set_spec.rb b/spec/models/compliance_control_set_spec.rb index c157dcaf3..a66e7f030 100644 --- a/spec/models/compliance_control_set_spec.rb +++ b/spec/models/compliance_control_set_spec.rb @@ -10,5 +10,5 @@ RSpec.describe ComplianceControlSet, type: :model do it { should have_many(:compliance_control_blocks).dependent(:destroy) } it { should validate_presence_of :name } - it { is_expected.to be_versioned } + end diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb index b92bcfbdb..1dfe6d33c 100644 --- a/spec/models/custom_field_spec.rb +++ b/spec/models/custom_field_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' RSpec.describe CustomField, type: :model do + let( :vj ){ create :vehicle_journey, custom_field_values: {energy: 99} } context "validates" do @@ -35,11 +36,43 @@ RSpec.describe CustomField, type: :model do end context "custom field_values for a resource" do + before do + create :custom_field, field_type: :integer, code: :energy, name: :energy + end + it { expect(vj.custom_field_value("energy")).to eq(99) } end + context "with a 'list' field_type" do + let!(:field){ [create(:custom_field, code: :energy, field_type: 'list', options: {list_values: %w(foo bar baz)})] } + let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: "1"} } + it "should cast the value" do + p vj.custom_fields + expect(vj.custom_fields[:energy].value).to eq 1 + expect(vj.custom_fields[:energy].display_value).to eq "bar" + end + + it "should validate the value" do + { + "1" => true, + 1 => true, + "azerty" => false, + "10" => false, + 10 => false + }.each do |val, valid| + vj = build :vehicle_journey, custom_field_values: {energy: val} + if valid + expect(vj.validate).to be_truthy + else + expect(vj.validate).to be_falsy + expect(vj.errors.messages[:"custom_fields.energy"]).to be_present + end + end + end + end + context "with an 'integer' field_type" do - let!(:field){ [create(:custom_field, code: :energy, options: {field_type: 'integer'})] } + let!(:field){ [create(:custom_field, code: :energy, field_type: 'integer')] } let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: "99"} } it "should cast the value" do expect(vj.custom_fields[:energy].value).to eq 99 @@ -47,6 +80,7 @@ RSpec.describe CustomField, type: :model do it "should validate the value" do { + 99 => true, "99" => true, "azerty" => false, "91a" => false, @@ -64,10 +98,39 @@ RSpec.describe CustomField, type: :model do end context "with a 'string' field_type" do - let!(:field){ [create(:custom_field, code: :energy, options: {field_type: 'string'})] } + let!(:field){ [create(:custom_field, code: :energy, field_type: 'string')] } let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: 99} } it "should cast the value" do expect(vj.custom_fields[:energy].value).to eq '99' end end + + context "with a 'attachment' field_type" do + let!(:field){ [create(:custom_field, code: :energy, field_type: 'attachment')] } + let( :vj ){ create :vehicle_journey, custom_field_values: {energy: File.open(Rails.root.join('spec', 'fixtures', 'users.json'))} } + it "should cast the value" do + expect(vj.custom_fields[:energy].value.class).to be CustomFieldAttachmentUploader + path = vj.custom_fields[:energy].value.path + expect(File.exists?(path)).to be_truthy + expect(vj).to receive(:remove_custom_field_energy!).and_call_original + vj.destroy + vj.run_callbacks(:commit) + expect(File.exists?(path)).to be_falsy + end + + it "should display a link" do + val = vj.custom_fields[:energy].value + out = vj.custom_fields[:energy].display_value + expect(out).to match(val.url) + expect(out).to match(/\<a.*\>/) + end + + context "with a whitelist" do + let!(:field){ [create(:custom_field, code: :energy, field_type: 'attachment', options: {extension_whitelist: %w(zip)})] } + it "should validate extension" do + expect(build(:vehicle_journey, custom_field_values: {energy: File.open(Rails.root.join('spec', 'fixtures', 'users.json'))})).to_not be_valid + expect(build(:vehicle_journey, custom_field_values: {energy: File.open(Rails.root.join('spec', 'fixtures', 'nozip.zip'))})).to be_valid + end + end + end end diff --git a/spec/models/referential_metadata_spec.rb b/spec/models/referential_metadata_spec.rb index 291ed974a..88a12b2bb 100644 --- a/spec/models/referential_metadata_spec.rb +++ b/spec/models/referential_metadata_spec.rb @@ -12,14 +12,19 @@ RSpec.describe ReferentialMetadata, :type => :model do describe ".new_from" do let(:referential_metadata) { create :referential_metadata, referential_source: create(:referential) } - let(:new_referential_metadata) { ReferentialMetadata.new_from(referential_metadata, []) } + let(:new_referential_metadata) { ReferentialMetadata.new_from(referential_metadata, nil) } + before do + referential_metadata.line_ids.each do |id| + Chouette::Line.find(id).update_attribute :line_referential_id, referential_metadata.referential.line_referential_id + end + end it "should not have an associated referential" do expect(new_referential_metadata).to be_a_new(ReferentialMetadata) end - xit "should have the same lines" do - expect(new_referential_metadata.lines).to eq(referential_metadata.lines) + it "should have the same lines" do + expect(new_referential_metadata.line_ids.sort).to eq(referential_metadata.line_ids.sort) end it "should have the same periods" do @@ -34,6 +39,14 @@ RSpec.describe ReferentialMetadata, :type => :model do expect(new_referential_metadata.referential_source).to eq(referential_metadata.referential) end + context "with a functional scope" do + let(:organisation){ create :organisation, sso_attributes: {"functional_scope" => [referential_metadata.referential.lines.first.objectid]} } + let(:new_referential_metadata) { ReferentialMetadata.new_from(referential_metadata, organisation) } + + it "should scope the lines" do + expect(new_referential_metadata.line_ids).to eq [referential_metadata.referential.lines.first.id] + end + end end describe "Period" do diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb index 1d9b3d78a..ca2caf57f 100644 --- a/spec/models/referential_spec.rb +++ b/spec/models/referential_spec.rb @@ -55,7 +55,7 @@ describe Referential, :type => :model do context "Cloning referential" do let(:clone) do - Referential.new_from(ref, []) + Referential.new_from(ref, nil) end let!(:workbench){ create :workbench } diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb new file mode 100644 index 000000000..b407cd866 --- /dev/null +++ b/spec/models/route_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +RSpec.describe Chouette::Route, :type => :model do + subject(:route){ create :route } + context "metadatas" do + it "should be empty at first" do + expect(Chouette::Route.has_metadata?).to be_truthy + expect(route.has_metadata?).to be_truthy + expect(route.metadata.creator_username).to be_nil + expect(route.metadata.modifier_username).to be_nil + end + + context "once set" do + it "should set the correct values" do + Timecop.freeze(Time.now) do + route.metadata.creator_username = "john.doe" + route.save! + id = route.id + route = Chouette::Route.find id + expect(route.metadata.creator_username).to eq "john.doe" + expect(route.metadata.creator_username_updated_at.strftime('%Y-%m-%d %H:%M:%S.%3N')).to eq Time.now.strftime('%Y-%m-%d %H:%M:%S.%3N') + end + end + end + + describe "#merge_metadata_from" do + let(:source){ create :route } + let(:metadata){ target.merge_metadata_from(source).metadata } + let(:target){ create :route } + before do + target.metadata.creator_username = "john" + target.metadata.modifier_username = "john" + end + context "when the source has no metadata" do + it "should do nothing" do + expect(metadata.creator_username).to eq "john" + expect(metadata.modifier_username).to eq "john" + end + end + + context "when the source has older metadata" do + before do + source.metadata.creator_username = "jane" + source.metadata.modifier_username = "jane" + source.metadata.creator_username_updated_at = 1.month.ago + source.metadata.modifier_username_updated_at = 1.month.ago + end + it "should do nothing" do + expect(metadata.creator_username).to eq "john" + expect(metadata.modifier_username).to eq "john" + end + end + + context "when the source has new metadata" do + before do + source.metadata.creator_username = "jane" + source.metadata.modifier_username = "jane" + target.metadata.creator_username_updated_at = 1.month.ago + target.metadata.modifier_username_updated_at = 1.month.ago + end + it "should update metadata" do + expect(metadata.creator_username).to eq "jane" + expect(metadata.modifier_username).to eq "jane" + end + end + end + end +end diff --git a/spec/services/route_way_cost_calculator_spec.rb b/spec/services/route_way_cost_calculator_spec.rb index d5358fcf6..79b81e34d 100644 --- a/spec/services/route_way_cost_calculator_spec.rb +++ b/spec/services/route_way_cost_calculator_spec.rb @@ -7,18 +7,20 @@ RSpec.describe RouteWayCostCalculator do # things in the request or response. This is just to fake the request so # we don't actually call their API in tests. The test doesn't test # anything given in the response. - stub_request(:post, "https://api.tomtom.com/routing/1/batch/json?key") + stub_request( + :post, + "https://api.tomtom.com/routing/1/matrix/json?key&routeType=shortest&travelMode=bus" + ) .with( headers: { 'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2' - } - ) + }) .to_return( status: 200, - body: "{\"formatVersion\":\"0.0.1\",\"batchItems\":[{\"statusCode\":200,\"response\":{\"routes\":[{\"summary\":{\"lengthInMeters\":117947,\"travelTimeInSeconds\":7969,\"trafficDelayInSeconds\":0,\"departureTime\":\"2018-03-12T12:32:26+01:00\",\"arrivalTime\":\"2018-03-12T14:45:14+01:00\"}}]}}]}", + body: "{\"formatVersion\":\"0.0.1\",\"matrix\":[[{\"statusCode\":200,\"response\":{\"routeSummary\":{\"lengthInMeters\":0,\"travelTimeInSeconds\":0,\"trafficDelayInSeconds\":0,\"departureTime\":\"2018-03-23T11:20:17+01:00\",\"arrivalTime\":\"2018-03-23T11:20:17+01:00\"}}}]]}", headers: {} ) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cde252236..947efd602 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -34,7 +34,6 @@ require 'webmock/rspec' require 'simplecov' require 'sidekiq/testing' Sidekiq::Testing.fake! -require 'paper_trail/frameworks/rspec' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are diff --git a/spec/support/referential.rb b/spec/support/referential.rb index 9acdce73a..b50844ae4 100644 --- a/spec/support/referential.rb +++ b/spec/support/referential.rb @@ -8,6 +8,10 @@ module ReferentialHelper Organisation.find_by!(code: "first") end + def first_workgroup + Workgroup.find_by_name('IDFM') + end + def self.included(base) base.class_eval do extend ClassMethods @@ -53,10 +57,18 @@ RSpec.configure do |config| referential.add_member organisation, owner: true end + workgroup = FactoryGirl.create( + :workgroup, + name: "IDFM", + line_referential: line_referential, + stop_area_referential: stop_area_referential + ) + workbench = FactoryGirl.create( :workbench, name: "Gestion de l'offre", organisation: organisation, + workgroup: workgroup, line_referential: line_referential, stop_area_referential: stop_area_referential ) diff --git a/spec/views/companies/edit.html.erb_spec.rb b/spec/views/companies/edit.html.erb_spec.rb index 8aaf705ab..c72d84c0b 100644 --- a/spec/views/companies/edit.html.erb_spec.rb +++ b/spec/views/companies/edit.html.erb_spec.rb @@ -5,7 +5,10 @@ describe "/companies/edit", :type => :view do let!(:company) { assign(:company, create(:company)) } let!(:companies) { Array.new(2) { create(:company) } } let!(:line_referential) { assign :line_referential, company.line_referential } - + before do + allow(view).to receive(:resource){ company } + allow(view).to receive(:current_referential){ first_referential } + end describe "form" do it "should render input for name" do render diff --git a/spec/views/companies/new.html.erb_spec.rb b/spec/views/companies/new.html.erb_spec.rb index ebb8c03c5..6c2163677 100644 --- a/spec/views/companies/new.html.erb_spec.rb +++ b/spec/views/companies/new.html.erb_spec.rb @@ -4,7 +4,10 @@ describe "/companies/new", :type => :view do let!(:company) { assign(:company, build(:company)) } let!(:line_referential) { assign :line_referential, company.line_referential } - + before do + allow(view).to receive(:resource){company} + allow(view).to receive(:current_referential){ first_referential } + end describe "form" do it "should render input for name" do diff --git a/spec/views/stop_areas/edit.html.erb_spec.rb b/spec/views/stop_areas/edit.html.erb_spec.rb index bfbb0bb55..1bdb42817 100644 --- a/spec/views/stop_areas/edit.html.erb_spec.rb +++ b/spec/views/stop_areas/edit.html.erb_spec.rb @@ -8,6 +8,7 @@ describe "/stop_areas/edit", :type => :view do before do allow(view).to receive(:has_feature?) + allow(view).to receive(:resource){ stop_area } end describe "form" do diff --git a/spec/views/stop_areas/new.html.erb_spec.rb b/spec/views/stop_areas/new.html.erb_spec.rb index 23f7387fa..eced129e4 100644 --- a/spec/views/stop_areas/new.html.erb_spec.rb +++ b/spec/views/stop_areas/new.html.erb_spec.rb @@ -7,6 +7,7 @@ describe "/stop_areas/new", :type => :view do before do allow(view).to receive(:has_feature?) + allow(view).to receive(:resource){ stop_area } end describe "form" do |
