diff options
55 files changed, 416 insertions, 123 deletions
@@ -125,7 +125,7 @@ gem 'enumerize', '~> 2.1.2' gem 'deep_cloneable', '~> 2.0.0' gem 'acts-as-taggable-on', '~> 4.0.0' -gem 'acts_as_list', '~> 0.6.0' +gem 'acts_as_list', '~> 0.9.11' gem 'acts_as_tree', '~> 2.1.0', require: 'acts_as_tree' gem 'rabl' diff --git a/Gemfile.lock b/Gemfile.lock index 4fb77eeb9..2071eee78 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -86,7 +86,7 @@ GEM tzinfo (~> 1.1) acts-as-taggable-on (4.0.0) activerecord (>= 4.0) - acts_as_list (0.6.0) + acts_as_list (0.9.11) activerecord (>= 3.0) acts_as_tree (2.1.0) activerecord (>= 3.0.0) @@ -593,7 +593,7 @@ DEPENDENCIES active_attr activerecord-postgis-adapter (~> 3.0.0) acts-as-taggable-on (~> 4.0.0) - acts_as_list (~> 0.6.0) + acts_as_list (~> 0.9.11) acts_as_tree (~> 2.1.0) apartment (~> 1.0.0) awesome_print diff --git a/app/assets/javascripts/smart_date.coffee b/app/assets/javascripts/smart_date.coffee index 48aa1c2f9..9c8b44207 100644 --- a/app/assets/javascripts/smart_date.coffee +++ b/app/assets/javascripts/smart_date.coffee @@ -14,11 +14,16 @@ window.isLeapYear = (year) -> window.smartCorrectDate = -> allSelectors = $(@).parent().children('select') - allVals = allSelectors.map (index, sel) -> - parseInt($(sel).val()) + + yearSelect = allSelectors.filter("[name$='(1i)]']") + monthSelect = allSelectors.filter("[name$='(2i)]']") + daySelect = allSelectors.filter("[name$='(3i)]']") + # We expect [day, month, year], so french + allVals = [daySelect, monthSelect, yearSelect].map (sel, index) -> + parseInt(sel.val()) + correctedDay = correctDay allVals - daySelector = allSelectors.first() - $(daySelector).val(correctedDay) + daySelect.val(correctedDay) $ -> $(document).on 'change', '.smart_date select', smartCorrectDate diff --git a/app/controllers/concerns/metadata_controller_support.rb b/app/controllers/concerns/metadata_controller_support.rb index db83e79ae..4dcbfe5d0 100644 --- a/app/controllers/concerns/metadata_controller_support.rb +++ b/app/controllers/concerns/metadata_controller_support.rb @@ -20,7 +20,7 @@ module MetadataControllerSupport def set_modifier_metadata _resource = @resources || [resource] _resource.flatten.each do |r| - r.try :set_metadata!, :modifier_username, user_for_metadata + r.try(:set_metadata!, :modifier_username, user_for_metadata) if r.valid? end end end diff --git a/app/controllers/journey_patterns_collections_controller.rb b/app/controllers/journey_patterns_collections_controller.rb index db92d48f3..c1a307464 100644 --- a/app/controllers/journey_patterns_collections_controller.rb +++ b/app/controllers/journey_patterns_collections_controller.rb @@ -25,6 +25,8 @@ class JourneyPatternsCollectionsController < ChouetteController @q = @q.includes(:stop_points) @ppage = 10 @journey_patterns ||= @q.paginate(page: params[:page], per_page: @ppage).order(:name) + @custom_fields = Chouette::JourneyPattern.custom_fields_definitions(referential.workgroup) + respond_to do |format| format.json do @journey_patterns = @journey_patterns.includes(stop_points: {stop_area: :stop_area_referential}) diff --git a/app/controllers/merges_controller.rb b/app/controllers/merges_controller.rb index 1ce64ed58..663b6e750 100644 --- a/app/controllers/merges_controller.rb +++ b/app/controllers/merges_controller.rb @@ -1,5 +1,5 @@ class MergesController < ChouetteController - # include PolicyChecker + include PolicyChecker defaults resource_class: Merge belongs_to :workbench diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index 2ac8532e0..4ca2293f0 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -77,7 +77,7 @@ class TimeTablesController < ChouetteController end def index - request.format.kml? ? @per_page = nil : @per_page = 12 + # request.format.kml? ? @per_page = nil : @per_page = 12 index! do |format| format.html { @@ -130,6 +130,7 @@ class TimeTablesController < ChouetteController @time_tables ||= begin time_tables = @q.result(:distinct => true) + sort_column if sort_column == "bounding_dates" time_tables = @q.result(:distinct => false).paginate(page: params[:page], per_page: 10) ids = time_tables.pluck(:id).uniq @@ -186,10 +187,13 @@ class TimeTablesController < ChouetteController private def sort_column - valid_cols = referential.time_tables.column_names - valid_cols << "bounding_dates" - valid_cols << "vehicle_journeys_count" - valid_cols.include?(params[:sort]) ? params[:sort] : 'comment' + @@valid_cols ||= begin + valid_cols = %w(id color comment) + valid_cols << "bounding_dates" + valid_cols << "vehicle_journeys_count" + valid_cols + end + @@valid_cols.include?(params[:sort]) ? params[:sort] : 'comment' end def sort_direction %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc' diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb index 0e7b6b9ae..077978c36 100644 --- a/app/decorators/line_decorator.rb +++ b/app/decorators/line_decorator.rb @@ -35,11 +35,6 @@ class LineDecorator < AF83::Decorator edit_action_link do |l| l.content {|l| l.primary? ? h.t('actions.edit') : h.t('lines.actions.edit') } end - - action_link on: :index, secondary: :index do |l| - l.content t('lines.actions.new') - l.href { h.new_line_referential_line_path(context[:line_referential]) } - end end ### the option :policy will automatically check for the corresponding method diff --git a/app/helpers/multiple_selection_toolbox_helper.rb b/app/helpers/multiple_selection_toolbox_helper.rb index 7e02c6d73..012851b4a 100644 --- a/app/helpers/multiple_selection_toolbox_helper.rb +++ b/app/helpers/multiple_selection_toolbox_helper.rb @@ -4,7 +4,7 @@ module MultipleSelectionToolboxHelper # #5206 method too long def multiple_selection_toolbox(actions, collection_name:) links = content_tag :ul do - + # #5206 `if params[:controller]` mieux passer comme parametre si besoin delete_path = nil @@ -19,8 +19,7 @@ module MultipleSelectionToolboxHelper method: :delete, data: { path: delete_path, - # #5206 Missing Translations - confirm: t('actions.are_you_sure') + confirm: t('are_you_sure') }, title: t("actions.#{action}") ) do @@ -38,7 +37,7 @@ module MultipleSelectionToolboxHelper class: 'info-msg' ) - content_tag :div, '', + content_tag :div, '', class: 'select_toolbox noselect', id: "selected-#{collection_name}-action-box" do links + label diff --git a/app/helpers/referentials_helper.rb b/app/helpers/referentials_helper.rb index 9c3852322..a01d901e6 100644 --- a/app/helpers/referentials_helper.rb +++ b/app/helpers/referentials_helper.rb @@ -1,13 +1,15 @@ module ReferentialsHelper # Outputs a green check icon and the text "Oui" or a red exclamation mark # icon and the text "Non" based on `status` - def line_status(status) + def line_status(status, verbose=true) if status - content_tag(:span, nil, class: 'fa fa-exclamation-circle fa-lg text-danger') + - t('activerecord.attributes.line.deactivated') + out = content_tag(:span, nil, class: 'fa fa-exclamation-circle fa-lg text-danger') + out += t('activerecord.attributes.line.deactivated') if verbose + out else - content_tag(:span, nil, class: 'fa fa-check-circle fa-lg text-success') + - t('activerecord.attributes.line.activated') + out = content_tag(:span, nil, class: 'fa fa-check-circle fa-lg text-success') + out += t('activerecord.attributes.line.activated') if verbose + out end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index be70d974d..16081b660 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -15,7 +15,7 @@ module SearchHelper if val.is_a?(Array) active = val.any? &:present? elsif val.is_a?(Hash) - active = val.values.any? &:present? + active = val.values.any? {|v| v.present? && v != "false" } else active = true end diff --git a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js b/app/javascript/helpers/CustomFieldsInputs.js index 827c36b76..abc8097d5 100644 --- a/app/javascript/vehicle_journeys/components/tools/CustomFieldsInputs.js +++ b/app/javascript/helpers/CustomFieldsInputs.js @@ -34,8 +34,21 @@ export default class CustomFieldsInputs extends Component { 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) } + value={cf.value} + onChange={(e) => {this.props.onUpdate(cf.code, e.target.value); this.forceUpdate()} } + /> + ) + } + + integerInput(cf){ + return( + <input + type='number' + ref={'custom_fields.' + cf.code} + className='form-control' + disabled={this.props.disabled} + value={cf.value} + onChange={(e) => {this.props.onUpdate(cf.code, e.target.value); this.forceUpdate()} } /> ) } diff --git a/app/javascript/journey_patterns/components/CreateModal.js b/app/javascript/journey_patterns/components/CreateModal.js index 946c13d9c..51f6f6c1b 100644 --- a/app/javascript/journey_patterns/components/CreateModal.js +++ b/app/javascript/journey_patterns/components/CreateModal.js @@ -1,15 +1,17 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import actions from '../actions' +import CustomFieldsInputs from '../../helpers/CustomFieldsInputs' export default class CreateModal extends Component { constructor(props) { super(props) + this.custom_fields = _.assign({}, this.props.custom_fields) } handleSubmit() { if(actions.validateFields(this.refs) == true) { - this.props.onAddJourneyPattern(this.refs) + this.props.onAddJourneyPattern(_.assign({}, this.refs, {custom_fields: this.custom_fields})) this.props.onModalClose() $('#NewJourneyPatternModal').modal('hide') } @@ -78,8 +80,14 @@ export default class CreateModal extends Component { /> </div> </div> + <CustomFieldsInputs + values={this.props.custom_fields} + onUpdate={(code, value) => this.custom_fields[code]["value"] = value} + disabled={false} + /> </div> </div> + <div className='modal-footer'> <button className='btn btn-link' diff --git a/app/javascript/journey_patterns/components/EditModal.js b/app/javascript/journey_patterns/components/EditModal.js index 1960849fb..a23a57f96 100644 --- a/app/javascript/journey_patterns/components/EditModal.js +++ b/app/javascript/journey_patterns/components/EditModal.js @@ -1,19 +1,27 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import actions from '../actions' +import CustomFieldsInputs from '../../helpers/CustomFieldsInputs' export default class EditModal extends Component { constructor(props) { super(props) + this.updateValue = this.updateValue.bind(this) } handleSubmit() { if(actions.validateFields(this.refs) == true) { - this.props.saveModal(this.props.modal.modalProps.index, this.refs) + this.props.saveModal(this.props.modal.modalProps.index, _.assign({}, this.refs, {custom_fields: this.custom_fields})) $('#JourneyPatternModal').modal('hide') } } + updateValue(attribute, e) { + actions.resetValidation(e.currentTarget) + this.props.modal.modalProps.journeyPattern[attribute] = e.target.value + this.forceUpdate() + } + renderModalTitle() { if (this.props.editMode) { return ( @@ -28,6 +36,9 @@ export default class EditModal extends Component { } render() { + if(this.props.modal.modalProps.journeyPattern){ + this.custom_fields = _.assign({}, this.props.modal.modalProps.journeyPattern.custom_fields) + } return ( <div className={ 'modal fade ' + ((this.props.modal.type == 'edit') ? 'in' : '') } id='JourneyPatternModal'> <div className='modal-container'> @@ -48,8 +59,8 @@ export default class EditModal extends Component { className='form-control' disabled={!this.props.editMode} id={this.props.modal.modalProps.index} - defaultValue={this.props.modal.modalProps.journeyPattern.name} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + value={this.props.modal.modalProps.journeyPattern.name} + onChange={(e) => this.updateValue('name', e)} required /> </div> @@ -64,8 +75,8 @@ export default class EditModal extends Component { className='form-control' disabled={!this.props.editMode} id={this.props.modal.modalProps.index} - defaultValue={this.props.modal.modalProps.journeyPattern.published_name} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + value={this.props.modal.modalProps.journeyPattern.published_name} + onChange={(e) => this.updateValue('published_name', e)} required /> </div> @@ -79,12 +90,19 @@ export default class EditModal extends Component { className='form-control' disabled={!this.props.editMode} id={this.props.modal.modalProps.index} - defaultValue={this.props.modal.modalProps.journeyPattern.registration_number} - onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + value={this.props.modal.modalProps.journeyPattern.registration_number} + onChange={(e) => this.updateValue('registration_number', e)} /> </div> </div> </div> + <div className='row'> + <CustomFieldsInputs + values={this.props.modal.modalProps.journeyPattern.custom_fields} + onUpdate={(code, value) => this.custom_fields[code]["value"] = value} + disabled={!this.props.editMode} + /> + </div> <div> <label className='control-label'>{I18n.attribute_name('journey_pattern', 'checksum')}</label> <input @@ -92,7 +110,7 @@ export default class EditModal extends Component { ref='checksum' className='form-control' disabled='disabled' - defaultValue={this.props.modal.modalProps.journeyPattern.checksum} + value={this.props.modal.modalProps.journeyPattern.checksum} /> </div> </div> diff --git a/app/javascript/journey_patterns/containers/AddJourneyPattern.js b/app/javascript/journey_patterns/containers/AddJourneyPattern.js index b093fd111..9e85afd5e 100644 --- a/app/javascript/journey_patterns/containers/AddJourneyPattern.js +++ b/app/javascript/journey_patterns/containers/AddJourneyPattern.js @@ -7,7 +7,8 @@ const mapStateToProps = (state) => { modal: state.modal, journeyPatterns: state.journeyPatterns, editMode: state.editMode, - status: state.status + status: state.status, + custom_fields: state.custom_fields } } diff --git a/app/javascript/journey_patterns/containers/Modal.js b/app/javascript/journey_patterns/containers/Modal.js index 33ee8583c..fc04843e4 100644 --- a/app/javascript/journey_patterns/containers/Modal.js +++ b/app/javascript/journey_patterns/containers/Modal.js @@ -7,7 +7,8 @@ const mapStateToProps = (state) => { return { editMode: state.editMode, modal: state.modal, - journeyPattern: state.journeyPattern + journeyPattern: state.journeyPattern, + custom_fields: state.custom_fields, } } diff --git a/app/javascript/journey_patterns/reducers/index.js b/app/javascript/journey_patterns/reducers/index.js index 2ffaf86d4..d3a1d29a2 100644 --- a/app/javascript/journey_patterns/reducers/index.js +++ b/app/javascript/journey_patterns/reducers/index.js @@ -12,7 +12,8 @@ const journeyPatternsApp = combineReducers({ journeyPatterns, pagination, stopPointsList, - modal + modal, + custom_fields: (state = [], action) => state }) export default journeyPatternsApp diff --git a/app/javascript/journey_patterns/reducers/journeyPatterns.js b/app/javascript/journey_patterns/reducers/journeyPatterns.js index b046f2b38..1a6a27da6 100644 --- a/app/javascript/journey_patterns/reducers/journeyPatterns.js +++ b/app/javascript/journey_patterns/reducers/journeyPatterns.js @@ -103,7 +103,8 @@ export default function journeyPatterns (state = [], action) { return _.assign({}, j, { name: action.data.name.value, published_name: action.data.published_name.value, - registration_number: action.data.registration_number.value + registration_number: action.data.registration_number.value, + custom_fields: action.data.custom_fields, }) } else { return j diff --git a/app/javascript/packs/journey_patterns/index.js b/app/javascript/packs/journey_patterns/index.js index 075eea13a..bd7df2634 100644 --- a/app/javascript/packs/journey_patterns/index.js +++ b/app/javascript/packs/journey_patterns/index.js @@ -34,7 +34,8 @@ var initialState = { type: '', modalProps: {}, confirmModal: {} - } + }, + custom_fields: window.custom_fields } // const loggerMiddleware = createLogger() diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js index d9746a379..186af540a 100644 --- a/app/javascript/time_tables/components/Metas.js +++ b/app/javascript/time_tables/components/Metas.js @@ -76,7 +76,6 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'tag_list')}</label> <div className="col-sm-8"> <TagsSelect2 - initialTags={metas.initial_tags} tags={metas.tags} onSelect2Tags={(e) => onSelect2Tags(e)} onUnselect2Tags={(e) => onUnselect2Tags(e)} diff --git a/app/javascript/time_tables/components/TagsSelect2.js b/app/javascript/time_tables/components/TagsSelect2.js index dd8d6e9c0..fe610ed58 100644 --- a/app/javascript/time_tables/components/TagsSelect2.js +++ b/app/javascript/time_tables/components/TagsSelect2.js @@ -27,7 +27,7 @@ export default class TagsSelect2 extends Component { return ( <Select2 value={(this.props.tags.length) ? map(this.props.tags, 'id') : undefined} - data={(this.props.initialTags.length) ? this.mapKeys(this.props.initialTags) : undefined} + data={(this.props.tags.length) ? this.mapKeys(this.props.tags) : undefined} onSelect={(e) => this.props.onSelect2Tags(e)} onUnselect={(e) => setTimeout( () => this.props.onUnselect2Tags(e, 150))} multiple={true} @@ -74,4 +74,4 @@ export default class TagsSelect2 extends Component { const formatRepo = (props) => { if(props.name) return props.name -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/containers/Metas.js b/app/javascript/time_tables/containers/Metas.js index ebccf556e..7bc3ef4e1 100644 --- a/app/javascript/time_tables/containers/Metas.js +++ b/app/javascript/time_tables/containers/Metas.js @@ -24,6 +24,7 @@ const mapDispatchToProps = (dispatch) => { }, onSelect2Tags: (e) => { e.preventDefault() + $(e.target).find('[data-select2-tag]').remove() dispatch(actions.select2Tags(e.params.data)) }, onUnselect2Tags: (e) => { diff --git a/app/javascript/time_tables/reducers/metas.js b/app/javascript/time_tables/reducers/metas.js index 51e1ec149..012f29511 100644 --- a/app/javascript/time_tables/reducers/metas.js +++ b/app/javascript/time_tables/reducers/metas.js @@ -31,11 +31,13 @@ export default function metas(state = {}, action) { return assign({}, state, {color: action.color}) case 'UPDATE_SELECT_TAG': let tags = [...state.tags] - tags.push(action.selectedItem) + if(tags.length == 0 || tags[tags.length-1].name != action.selectedItem.name){ + tags.push(action.selectedItem) + } return assign({}, state, {tags: tags}) case 'UPDATE_UNSELECT_TAG': return assign({}, state, {tags: filter(state.tags, (t) => (t.id != action.selectedItem.id))}) default: return state } -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index c6f59ce9d..e4f5ad11c 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -87,16 +87,18 @@ export default class VehicleJourneys extends Component { } toggleTimetables(e) { - $('.table-2entries .detailed-timetables').toggleClass('hidden') - $('.table-2entries .detailed-timetables-bt').toggleClass('active') + let root = $(this.refs['vehicleJourneys']) + root.find('.table-2entries .detailed-timetables').toggleClass('hidden') + root.find('.table-2entries .detailed-timetables-bt').toggleClass('active') this.componentDidUpdate() e.preventDefault() false } togglePurchaseWindows(e) { - $('.table-2entries .detailed-purchase-windows').toggleClass('hidden') - $('.table-2entries .detailed-purchase-windows-bt').toggleClass('active') + let root = $(this.refs['vehicleJourneys']) + root.find('.table-2entries .detailed-purchase-windows').toggleClass('hidden') + root.find('.table-2entries .detailed-purchase-windows-bt').toggleClass('active') this.componentDidUpdate() e.preventDefault() false @@ -186,7 +188,7 @@ export default class VehicleJourneys extends Component { ) } else { return ( - <div className='row'> + <div className='row' ref='vehicleJourneys'> <div className='col-lg-12'> {(this.props.status.fetchSuccess == false) && ( <div className='alert alert-danger mt-sm'> diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js index f49b51f08..1d470cd43 100644 --- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js +++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import actions from '../../actions' import MissionSelect2 from './select2s/MissionSelect2' import CompanySelect2 from './select2s/CompanySelect2' -import CustomFieldsInputs from './CustomFieldsInputs' +import CustomFieldsInputs from '../../../helpers/CustomFieldsInputs' export default class CreateModal extends Component { constructor(props) { diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js index e4e266c79..60d982845 100644 --- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import actions from '../../actions' import CompanySelect2 from './select2s/CompanySelect2' -import CustomFieldsInputs from './CustomFieldsInputs' +import CustomFieldsInputs from '../../../helpers/CustomFieldsInputs' export default class EditVehicleJourney extends Component { constructor(props) { diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb index 830a6a808..5b5015107 100644 --- a/app/models/chouette/journey_pattern.rb +++ b/app/models/chouette/journey_pattern.rb @@ -2,6 +2,7 @@ module Chouette class JourneyPattern < Chouette::TridentActiveRecord has_metadata include ChecksumSupport + include CustomFieldsSupport include JourneyPatternRestrictions include ObjectidSupport @@ -52,12 +53,19 @@ module Chouette end def self.state_permited_attributes item - { + attrs = { name: item['name'], published_name: item['published_name'], registration_number: item['registration_number'], costs: item['costs'] } + attrs["custom_field_values"] = Hash[ + *(item["custom_fields"] || {}) + .map { |k, v| [k, v["value"]] } + .flatten + ] + + attrs end def self.state_create_instance route, item diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb index e10b106ec..d22674637 100644 --- a/app/models/chouette/purchase_window.rb +++ b/app/models/chouette/purchase_window.rb @@ -46,8 +46,9 @@ module Chouette ] end - # def checksum_attributes - # end - + def color + _color = read_attribute(:color) + _color.present? ? _color : nil + end end end diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 14bfa47b6..a5eab3002 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -7,11 +7,12 @@ module Chouette include ObjectidSupport extend Enumerize - if Rails.env.development? + if ENV["CHOUETTE_ROUTE_POSITION_CHECK"] == "true" || !Rails.env.production? after_commit do positions = stop_points.pluck(:position) + Rails.logger.debug "Check positions in Route #{id} : #{positions.inspect}" if positions.size != positions.uniq.size - raise "DUPLICATED stop_points positions: #{positions}" + raise "DUPLICATED stop_points positions in Route #{id} : #{positions.inspect}" end end end @@ -77,7 +78,13 @@ 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? } + after_commit :calculate_costs!, + on: [:create, :update], + if: ->() { + # Ensure the call back doesn't run during a referential merge + !referential.in_referential_suite? && + TomTom.enabled? + } def duplicate opposite=false overrides = { diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index 4ddc7403b..b933e1944 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -436,6 +436,12 @@ module Chouette ActiveSupport::TimeZone[time_zone]&.utc_offset end + def full_time_zone_name + return unless time_zone.present? + return unless ActiveSupport::TimeZone[time_zone].present? + ActiveSupport::TimeZone[time_zone].tzinfo.name + end + def country return unless country_code country = ISO3166::Country[country_code] diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index b59c95665..29e3808e7 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -16,7 +16,7 @@ module Chouette end ransacker :unaccented_comment, formatter: ->(val){ val.parameterize } do - Arel.sql('unaccent(comment)') + Arel.sql('unaccent(time_tables.comment)') end has_and_belongs_to_many :vehicle_journeys, :class_name => 'Chouette::VehicleJourney' @@ -81,6 +81,11 @@ module Chouette chunk.values.delete_if {|dates| dates.count < 2} end + def color + _color = read_attribute(:color) + _color.present? ? _color : nil + end + def convert_continuous_dates_to_periods chunks = self.continuous_dates diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 22118a15a..65eabb205 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -7,10 +7,6 @@ class CustomField < ApplicationModel validates :name, uniqueness: {scope: [:resource_type, :workgroup_id]} 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, workgroup=:all vals = object.class.custom_fields(workgroup).map do |v| diff --git a/app/models/import/gtfs.rb b/app/models/import/gtfs.rb index 70f448132..a20c468c1 100644 --- a/app/models/import/gtfs.rb +++ b/app/models/import/gtfs.rb @@ -56,12 +56,16 @@ class Import::Gtfs < Import::Base attr_accessor :download_host def download_host - @download_host ||= Rails.application.config.rails_host.gsub("http://","") + @download_host ||= Rails.application.config.rails_host end def local_temp_directory - Rails.application.config.try(:import_temporary_directory) || - Rails.root.join('tmp', 'imports') + @local_temp_directory ||= + begin + directory = Rails.application.config.try(:import_temporary_directory) || Rails.root.join('tmp', 'imports') + FileUtils.mkdir_p directory + directory + end end def local_temp_file(&block) @@ -75,11 +79,20 @@ class Import::Gtfs < Import::Base Rails.application.routes.url_helpers.download_workbench_import_path(workbench, id, token: token_download) end + def download_uri + @download_uri ||= + begin + host = download_host + host = "http://#{host}" unless host =~ %r{https?://} + URI.join(host, download_path) + end + end + def download_local_file local_temp_file do |file| begin - Net::HTTP.start(download_host) do |http| - http.request_get(download_path) do |response| + Net::HTTP.start(download_uri.host, download_uri.port) do |http| + http.request_get(download_uri.request_uri) do |response| response.read_body do |segment| file.write segment end diff --git a/app/models/merge.rb b/app/models/merge.rb index 6e2a7036a..8d661f209 100644 --- a/app/models/merge.rb +++ b/app/models/merge.rb @@ -50,7 +50,7 @@ class Merge < ApplicationModel new = if workbench.output.current Rails.logger.debug "Clone current output" - Referential.new_from(workbench.output.current, fixme_functional_scope).tap do |clone| + Referential.new_from(workbench.output.current, workbench.organisation).tap do |clone| clone.inline_clone = true end else @@ -371,7 +371,7 @@ class Merge < ApplicationModel def save_current output.update current: new, new: nil - output.current.update referential_suite: output + output.current.update referential_suite: output, ready: true referentials.update_all merged_at: created_at, archived_at: created_at end diff --git a/app/policies/merge_policy.rb b/app/policies/merge_policy.rb index 82eb72e08..154dc63f5 100644 --- a/app/policies/merge_policy.rb +++ b/app/policies/merge_policy.rb @@ -8,8 +8,4 @@ class MergePolicy < ApplicationPolicy def create? user.has_permission?('merges.create') end - - def update? - user.has_permission?('merges.update') - end end diff --git a/app/views/api/v1/journey_patterns/show.rabl b/app/views/api/v1/journey_patterns/show.rabl index aac66b6f3..d02781cfa 100644 --- a/app/views/api/v1/journey_patterns/show.rabl +++ b/app/views/api/v1/journey_patterns/show.rabl @@ -1,7 +1,7 @@ object @journey_pattern extends "api/v1/trident_objects/show" -[:id, :name, :published_name, :registration_number, :comment, :checksum].each do |attr| +[:id, :name, :published_name, :registration_number, :comment, :checksum, :custom_fields].each do |attr| attributes attr, :unless => lambda { |m| m.send( attr).nil?} end diff --git a/app/views/journey_patterns_collections/show.html.slim b/app/views/journey_patterns_collections/show.html.slim index b389a1da7..38c7f1b1b 100644 --- a/app/views/journey_patterns_collections/show.html.slim +++ b/app/views/journey_patterns_collections/show.html.slim @@ -20,5 +20,6 @@ | window.perms = #{raw @perms}; | window.features = #{raw @features}; | window.routeCostsUrl = "#{costs_referential_line_route_url(@referential, @route.line, @route, format: :json).html_safe}"; - + | window.custom_fields = #{(@custom_fields.to_json).html_safe}; + = javascript_pack_tag 'journey_patterns/index.js' diff --git a/app/views/lines/show.html.slim b/app/views/lines/show.html.slim index 9e1ae6d6f..b683b9be6 100644 --- a/app/views/lines/show.html.slim +++ b/app/views/lines/show.html.slim @@ -7,13 +7,13 @@ .col-lg-6.col-md-6.col-sm-12.col-xs-12 = definition_list t('metadatas'), { t('objectid') => @line.get_objectid.short_id, - @line.human_attribute_name(:deactivated) => (@line.deactivated? ? t('false') : t('true')), - @line.human_attribute_name(:network_id) => (@line.network.nil? ? t('lines.index.unset') : @line.network.name), - @line.human_attribute_name(:company_id) => (@line.company.nil? ? t('lines.index.unset') : @line.company.name), - @line.human_attribute_name(:secondary_companies) => (@line.secondary_companies.nil? ? t('lines.index.unset') : array_to_html_list(@line.secondary_companies.collect(&:name))), - @line.human_attribute_name(:number) => @line.number, - @line.human_attribute_name(:registration_number) => (@line.registration_number ? @line.registration_number : '-'), - @line.human_attribute_name(:transport_mode) => (@line.transport_mode.present? ? t("enumerize.transport_mode.#{@line.transport_mode}") : '-'), - @line.human_attribute_name(:transport_submode) => (@line.transport_submode.present? ? t("enumerize.transport_submode.#{@line.transport_submode}") : '-'), - @line.human_attribute_name(:url) => (@line.url ? @line.url : '-'), - @line.human_attribute_name(:seasonal) => (@line.seasonal? ? t('true') : t('false')),} + Chouette::Line.tmf(:state) => line_status(@line.deactivated), + Chouette::Line.tmf(:network_id) => (@line.network.nil? ? t('lines.index.unset') : @line.network.name), + Chouette::Line.tmf(:company_id) => (@line.company.nil? ? t('lines.index.unset') : @line.company.name), + Chouette::Line.tmf(:secondary_companies) => (@line.secondary_companies.nil? ? t('lines.index.unset') : array_to_html_list(@line.secondary_companies.collect(&:name))), + Chouette::Line.tmf(:number) => @line.number, + Chouette::Line.tmf(:registration_number) => (@line.registration_number ? @line.registration_number : '-'), + Chouette::Line.tmf(:transport_mode) => (@line.transport_mode.present? ? t("enumerize.transport_mode.#{@line.transport_mode}") : '-'), + Chouette::Line.tmf(:transport_submode) => (@line.transport_submode.present? ? t("enumerize.transport_submode.#{@line.transport_submode}") : '-'), + Chouette::Line.tmf(:url) => (@line.url ? @line.url : '-'), + Chouette::Line.tmf(:seasonal) => (@line.seasonal? ? t('true') : t('false')),} diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim index 91868a002..4804da527 100644 --- a/app/views/referential_lines/show.html.slim +++ b/app/views/referential_lines/show.html.slim @@ -7,10 +7,10 @@ .col-lg-6.col-md-6.col-sm-12.col-xs-12 = definition_list t('metadatas'), { t('id_codif') => @line.get_objectid.short_id, - Chouette::Line.tmf('activated') => (@line.deactivated? ? t('false') : t('true')), + Chouette::Line.tmf('state') => line_status(@line.deactivated), Chouette::Line.tmf('network_id') => (@line.network.nil? ? t('lines.index.unset') : link_to(@line.network.name, [@referential, @line.network]) ), Chouette::Line.tmf('company') => (@line.company.nil? ? t('lines.index.unset') : link_to(@line.company.name, [@referential, @line.company]) ), - Chouette::Line.tmf('secondary_company') => (@line.secondary_companies.nil? ? t('lines.index.unset') : @line.secondary_companies.collect(&:name).join(', ')), + Chouette::Line.tmf('secondary_companies') => (@line.secondary_companies.nil? ? t('lines.index.unset') : @line.secondary_companies.collect(&:name).join(', ')), Chouette::Line.tmf('registration_number') => @line.number, Chouette::Line.tmf('published_name') => (@line.registration_number ? @line.registration_number : '-'), Chouette::Line.tmf('transport_mode') => (@line.transport_mode.present? ? t("enumerize.transport_mode.#{@line.transport_mode}") : '-'), diff --git a/app/views/referentials/_period_fields.html.slim b/app/views/referentials/_period_fields.html.slim index 4d2372f7b..b0038c6b3 100644 --- a/app/views/referentials/_period_fields.html.slim +++ b/app/views/referentials/_period_fields.html.slim @@ -8,8 +8,8 @@ .wrapper div - = f.input :begin, as: :date, label: false, wrapper_html: { class: 'date smart_date' } + = f.input :begin, as: :date, label: false, start_year: Date.today.year - 15, end_year: Date.today.year + 15, wrapper_html: { class: 'date smart_date' } div - = f.input :end, as: :date, label: false, wrapper_html: { class: 'date smart_date' } + = f.input :end, as: :date, label: false, start_year: Date.today.year - 15, end_year: Date.today.year + 15, wrapper_html: { class: 'date smart_date' } div = link_to_remove_association '', f, class: 'fa fa-trash', data: { confirm: t('are_you_sure')}, title: t('actions.delete') diff --git a/app/views/stop_area_referentials/show.html.slim b/app/views/stop_area_referentials/show.html.slim index a76e39439..9e6925e2c 100644 --- a/app/views/stop_area_referentials/show.html.slim +++ b/app/views/stop_area_referentials/show.html.slim @@ -1,4 +1,5 @@ - breadcrumb :stop_area_referential, @stop_area_referential +- page_header_content_for @stop_area_referential - if policy(@stop_area_referential).synchronize? - content_for :page_header_actions do = link_to(t('actions.sync'), sync_stop_area_referential_path(@stop_area_referential), method: :post, class: 'btn btn-default') diff --git a/app/workers/route_way_cost_worker.rb b/app/workers/route_way_cost_worker.rb index d6bfed592..b62416c3d 100644 --- a/app/workers/route_way_cost_worker.rb +++ b/app/workers/route_way_cost_worker.rb @@ -7,10 +7,11 @@ class RouteWayCostWorker # Prevent recursive worker spawning since this call updates the # `costs` field of the route. - Chouette::Route.skip_callback(:save, :after, :calculate_costs!) - - RouteWayCostCalculator.new(route).calculate! - - Chouette::Route.set_callback(:save, :after, :calculate_costs!) + begin + Chouette::Route.skip_callback(:commit, :after, :calculate_costs!) + RouteWayCostCalculator.new(route).calculate! + ensure + Chouette::Route.set_callback(:commit, :after, :calculate_costs!) + end end end diff --git a/config/locales/lines.en.yml b/config/locales/lines.en.yml index 1cd5150db..3d1ddc149 100644 --- a/config/locales/lines.en.yml +++ b/config/locales/lines.en.yml @@ -78,7 +78,7 @@ en: name: "Network" company_id: "Company" company: "Company" - secondary_company: "Secondary company" + secondary_companies: "Secondary companies" companies: name: "Company" registration_number: "Registration number" diff --git a/config/locales/lines.fr.yml b/config/locales/lines.fr.yml index 6f4a2e9bf..47baf96f8 100644 --- a/config/locales/lines.fr.yml +++ b/config/locales/lines.fr.yml @@ -79,7 +79,7 @@ fr: name: "Réseau" company_id: "Transporteur principal" company: "Transporteur principal" - secondary_company: "Transporteurs secondaires" + secondary_companies: "Transporteurs secondaires" companies: name: "Transporteur principal" registration_number: "Nom court" diff --git a/config/locales/purchase_windows.fr.yml b/config/locales/purchase_windows.fr.yml index 3d5582ead..7f2ec259f 100644 --- a/config/locales/purchase_windows.fr.yml +++ b/config/locales/purchase_windows.fr.yml @@ -27,7 +27,7 @@ fr: show: "Consulter" edit: Editer destroy: Supprimer - destroy_confirm: Etes vous sûr de supprimer cet calendrier commercial ? + destroy_confirm: Etes vous sûr de vouloir supprimer ce calendrier commercial ? errors: overlapped_periods: Une autre période chevauche cette période short_period: "Une période doit être d'une durée de deux jours minimum" diff --git a/db/migrate/20180416065012_add_custom_field_values_to_journey_patterns.rb b/db/migrate/20180416065012_add_custom_field_values_to_journey_patterns.rb new file mode 100644 index 000000000..df114488c --- /dev/null +++ b/db/migrate/20180416065012_add_custom_field_values_to_journey_patterns.rb @@ -0,0 +1,5 @@ +class AddCustomFieldValuesToJourneyPatterns < ActiveRecord::Migration + def change + add_column :journey_patterns, :custom_field_values, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index 7e0e9c2b5..d20c9f1f7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,13 +11,14 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180319043333) do +ActiveRecord::Schema.define(version: 20180416065012) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - enable_extension "postgis" enable_extension "hstore" + enable_extension "postgis" enable_extension "unaccent" + enable_extension "objectid" create_table "access_links", id: :bigserial, force: :cascade do |t| t.integer "access_point_id", limit: 8 @@ -92,9 +93,9 @@ 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 @@ -121,6 +122,7 @@ 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 @@ -143,10 +145,11 @@ 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 "custom_field_values", default: {} t.jsonb "metadata", default: {} end + add_index "companies", ["line_referential_id", "registration_number"], name: "index_companies_on_referential_id_and_registration_number", using: :btree add_index "companies", ["line_referential_id"], name: "index_companies_on_line_referential_id", using: :btree add_index "companies", ["objectid"], name: "companies_objectid_key", unique: true, using: :btree add_index "companies", ["registration_number"], name: "companies_registration_number_key", using: :btree @@ -441,7 +444,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do add_index "import_messages", ["resource_id"], name: "index_import_messages_on_resource_id", using: :btree create_table "import_resources", id: :bigserial, force: :cascade do |t| - t.integer "import_id", limit: 8 + t.integer "import_id", limit: 8 t.string "status" t.datetime "created_at" t.datetime "updated_at" @@ -449,9 +452,13 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "reference" t.string "name" t.hstore "metrics" + t.integer "referential_id" + t.integer "parent_id" end add_index "import_resources", ["import_id"], name: "index_import_resources_on_import_id", using: :btree + add_index "import_resources", ["parent_id"], name: "index_import_resources_on_parent_id", using: :btree + add_index "import_resources", ["referential_id"], name: "index_import_resources_on_referential_id", using: :btree create_table "imports", id: :bigserial, force: :cascade do |t| t.string "status" @@ -509,6 +516,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.string "data_source_ref" t.json "costs" t.jsonb "metadata", default: {} + t.jsonb "custom_field_values" end add_index "journey_patterns", ["objectid"], name: "journey_patterns_objectid_key", unique: true, using: :btree @@ -585,6 +593,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.jsonb "metadata", default: {} end + add_index "lines", ["line_referential_id", "registration_number"], name: "index_lines_on_referential_id_and_registration_number", using: :btree add_index "lines", ["line_referential_id"], name: "index_lines_on_line_referential_id", using: :btree add_index "lines", ["objectid"], name: "lines_objectid_key", unique: true, using: :btree add_index "lines", ["registration_number"], name: "lines_registration_number_key", using: :btree @@ -857,14 +866,15 @@ ActiveRecord::Schema.define(version: 20180319043333) do t.integer "waiting_time" t.string "kind" t.jsonb "localized_names" + t.json "custom_field_values" t.datetime "confirmed_at" - t.jsonb "custom_field_values" t.jsonb "metadata", default: {} end add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree add_index "stop_areas", ["objectid"], name: "stop_areas_objectid_key", unique: true, using: :btree add_index "stop_areas", ["parent_id"], name: "index_stop_areas_on_parent_id", using: :btree + add_index "stop_areas", ["stop_area_referential_id", "registration_number"], name: "index_stop_areas_on_referential_id_and_registration_number", using: :btree add_index "stop_areas", ["stop_area_referential_id"], name: "index_stop_areas_on_stop_area_referential_id", using: :btree create_table "stop_areas_stop_areas", id: false, force: :cascade do |t| @@ -1058,9 +1068,9 @@ ActiveRecord::Schema.define(version: 20180319043333) do add_index "vehicle_journeys", ["route_id"], name: "index_vehicle_journeys_on_route_id", using: :btree create_table "versions", id: :bigserial, force: :cascade do |t| - t.string "item_type", null: false - t.integer "item_id", limit: 8, null: false - t.string "event", null: false + t.string "item_type", null: false + t.integer "item_id", null: false + t.string "event", null: false t.string "whodunnit" t.text "object" t.datetime "created_at" @@ -1114,6 +1124,7 @@ ActiveRecord::Schema.define(version: 20180319043333) do add_foreign_key "compliance_controls", "compliance_control_blocks" add_foreign_key "compliance_controls", "compliance_control_sets" add_foreign_key "group_of_lines_lines", "group_of_lines", name: "groupofline_group_fkey", on_delete: :cascade + add_foreign_key "import_resources", "referentials" add_foreign_key "journey_frequencies", "timebands", on_delete: :nullify add_foreign_key "journey_frequencies", "vehicle_journeys", on_delete: :nullify add_foreign_key "journey_patterns", "routes", name: "jp_route_fkey", on_delete: :cascade diff --git a/db/seeds/development/custom_fields.seeds.rb b/db/seeds/development/custom_fields.seeds.rb index eb3afc394..61a294bb0 100644 --- a/db/seeds/development/custom_fields.seeds.rb +++ b/db/seeds/development/custom_fields.seeds.rb @@ -40,7 +40,7 @@ Workgroup.find_each do |workgroup| workgroup.custom_fields.seed_by(code: "stop_area_test_integer") do |field| field.resource_type = "StopArea" - field.name = "Test de Nomber" + field.name = "Test de Nombre" field.field_type = "integer" end @@ -49,4 +49,10 @@ Workgroup.find_each do |workgroup| field.name = "Test de Piece Jointe" field.field_type = "attachment" end + + workgroup.custom_fields.seed_by(code: "journey_pattern_test_integer") do |field| + field.resource_type = "JourneyPattern" + field.name = "Test de Nombre" + field.field_type = "integer" + end end diff --git a/lib/tasks/checks.rake b/lib/tasks/checks.rake new file mode 100644 index 000000000..23c638964 --- /dev/null +++ b/lib/tasks/checks.rake @@ -0,0 +1,19 @@ +namespace :check do + desc "Check routes stop_points positions are valid" + task routes_integrity: :environment do + errors = [] + max = Referential.pluck(:name).map(&:size).max + 20 + Referential.find_each do |r| + r.switch do + Chouette::Route.find_each do |route| + positions = route.stop_points.pluck(:position) + if positions.size != positions.uniq.size + lb = "Referential: #{r.id}/#{r.name}" + errors << "#{lb + " "*(max-lb.size)} -> Wrong positions in Route #{route.id} : #{positions.inspect} " + end + end + end + end + puts errors.join("\n") + end +end diff --git a/spec/controllers/autocomplete_time_tables_controller_spec.rb b/spec/controllers/autocomplete_time_tables_controller_spec.rb index 85a8eb714..02bf83b6e 100644 --- a/spec/controllers/autocomplete_time_tables_controller_spec.rb +++ b/spec/controllers/autocomplete_time_tables_controller_spec.rb @@ -8,6 +8,11 @@ RSpec.describe AutocompleteTimeTablesController, type: :controller do let!(:time_table) { create :time_table, comment: 'écolà militaire' } let!(:blargh) { create :time_table, comment: 'écolàë militaire' } let!(:other_time_table) { create :time_table, comment: 'foo bar baz' } + let(:route){ create :route } + + before do + create :vehicle_journey, time_tables: [time_table, blargh, other_time_table], journey_pattern: route.full_journey_pattern + end describe 'GET #index' do it 'should be successful' do @@ -17,7 +22,7 @@ RSpec.describe AutocompleteTimeTablesController, type: :controller do context 'search by name' do it 'should be successful' do - get :index, referential_id: referential.id, q: {unaccented_comment_or_objectid_cont_any: 'écolà'}, :format => :json + get :index, referential_id: referential.id, q: {unaccented_comment_or_objectid_cont_any: 'écolà'}, format: :json expect(response).to be_success expect(assigns(:time_tables)).to include(time_table) expect(assigns(:time_tables)).to include(blargh) @@ -25,13 +30,33 @@ RSpec.describe AutocompleteTimeTablesController, type: :controller do end it 'should be accent insensitive' do - get :index, referential_id: referential.id, q: {unaccented_comment_or_objectid_cont_any: 'ecola'}, :format => :json + get :index, referential_id: referential.id, q: {unaccented_comment_or_objectid_cont_any: 'ecola'}, format: :json expect(response).to be_success expect(assigns(:time_tables)).to include(time_table) expect(assigns(:time_tables)).to include(blargh) expect(assigns(:time_tables)).to_not include(other_time_table) end end + + context "within a route" do + context 'search by name' do + it 'should be successful' do + get :index, referential_id: referential.id, q: {unaccented_comment_or_objectid_cont_any: 'écolà'}, route_id: route.id, format: :json + expect(response).to be_success + expect(assigns(:time_tables)).to include(time_table) + expect(assigns(:time_tables)).to include(blargh) + expect(assigns(:time_tables)).to_not include(other_time_table) + end + + it 'should be accent insensitive' do + get :index, referential_id: referential.id, q: {unaccented_comment_or_objectid_cont_any: 'ecola'}, route_id: route.id, format: :json + expect(response).to be_success + expect(assigns(:time_tables)).to include(time_table) + expect(assigns(:time_tables)).to include(blargh) + expect(assigns(:time_tables)).to_not include(other_time_table) + end + end + end end end diff --git a/spec/features/merges_permissions_spec.rb b/spec/features/merges_permissions_spec.rb new file mode 100644 index 000000000..e8af3b5e8 --- /dev/null +++ b/spec/features/merges_permissions_spec.rb @@ -0,0 +1,31 @@ +describe "Merges", :type => :feature do + login_user + + describe 'permissions' do + before do + allow_any_instance_of(MergePolicy).to receive(:create?).and_return permission + visit path + end + + describe 'on show view' do + let( :path ){ workbench_output_path(referential.workbench) } + let(:button_text) { I18n.t('merges.actions.create') } + + context 'if present → ' do + let( :permission ){ true } + it 'view shows the corresponding buttons' do + expected_new_url = new_workbench_merge_path(referential.workbench) + expect( page ).to have_link(button_text, href: expected_new_url) + end + end + + context 'if absent → ' do + let( :permission ){ false } + it 'view does not show the corresponding buttons' do + expect( page ).not_to have_link(button_text) + end + end + end + + end +end diff --git a/spec/models/chouette/route/route_base_spec.rb b/spec/models/chouette/route/route_base_spec.rb index 3d4a87791..e76f10a13 100644 --- a/spec/models/chouette/route/route_base_spec.rb +++ b/spec/models/chouette/route/route_base_spec.rb @@ -62,20 +62,39 @@ RSpec.describe Chouette::Route, :type => :model do end context "callbacks" do - it "calls #calculate_costs! after_save when TomTom is enabled" do + it "calls #calculate_costs! after_commit when TomTom is enabled", truncation: true do allow(TomTom).to receive(:enabled?).and_return(true) - route = create(:route) + route = build(:route) expect(route).to receive(:calculate_costs!) route.save end - it "doesn't call #calculate_costs! after_save if TomTom is disabled" do + it "doesn't call #calculate_costs! after_commit if TomTom is disabled", truncation: true do allow(TomTom).to receive(:enabled?).and_return(false) - route = create(:route) + route = build(:route) expect(route).not_to receive(:calculate_costs!) route.save end + + it "doesn't call #calculate_costs! after_commit if in a ReferentialSuite", + truncation: true do + begin + allow(TomTom).to receive(:enabled?).and_return(true) + + referential_suite = create(:referential_suite) + referential = create(:referential, referential_suite: referential_suite) + + referential.switch do + route = build(:route) + + expect(route).not_to receive(:calculate_costs!) + route.save + end + ensure + referential.destroy + end + end end end diff --git a/spec/models/import/gtfs_spec.rb b/spec/models/import/gtfs_spec.rb index b4b23be00..96b93dc62 100644 --- a/spec/models/import/gtfs_spec.rb +++ b/spec/models/import/gtfs_spec.rb @@ -262,10 +262,53 @@ RSpec.describe Import::Gtfs do end end - describe "#download_host" do - it "should return host defined by Rails.application.config.rails_host (without http:// schema)" do - allow(Rails.application.config).to receive(:rails_host).and_return("http://download_host") + describe "#download_uri" do + let(:import) { Import::Gtfs.new } + + before do + allow(import).to receive(:download_path).and_return("/download_path") + end + + context "when download_host is 'front'" do + before { allow(import).to receive(:download_host).and_return("front") } + it "returns http://front/download_path" do + expect(import.download_uri.to_s).to eq('http://front/download_path') + end + end + + context "when download_host is 'front:3000'" do + before { allow(import).to receive(:download_host).and_return("front:3000") } + it "returns http://front:3000/download_path" do + expect(import.download_uri.to_s).to eq('http://front:3000/download_path') + end + end + + context "when download_host is 'http://front:3000'" do + before { allow(import).to receive(:download_host).and_return("http://front:3000") } + it "returns http://front:3000/download_path" do + expect(import.download_uri.to_s).to eq('http://front:3000/download_path') + end + end + + context "when download_host is 'https://front:3000'" do + before { allow(import).to receive(:download_host).and_return("https://front:3000") } + it "returns https://front:3000/download_path" do + expect(import.download_uri.to_s).to eq('https://front:3000/download_path') + end + end + context "when download_host is 'http://front'" do + before { allow(import).to receive(:download_host).and_return("http://front") } + it "returns http://front/download_path" do + expect(import.download_uri.to_s).to eq('http://front/download_path') + end + end + + end + + describe "#download_host" do + it "should return host defined by Rails.application.config.rails_host" do + allow(Rails.application.config).to receive(:rails_host).and_return("download_host") expect(Import::Gtfs.new.download_host).to eq("download_host") end end diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb index b407cd866..58524038b 100644 --- a/spec/models/route_spec.rb +++ b/spec/models/route_spec.rb @@ -65,4 +65,39 @@ RSpec.describe Chouette::Route, :type => :model do end end end + + context "when creating stop_points" do + # Here we tests that acts_as_list does not mess with the positions + let(:stop_areas){ + 4.times.map{create :stop_area} + } + + it "should set a correct order to the stop_points" do + + order = [0, 3, 2, 1] + new = Referential.new + new.name = "mkmkm" + new.organisation = create(:organisation) + new.line_referential = create(:line_referential) + create(:line, line_referential: new.line_referential) + new.stop_area_referential = create(:stop_area_referential) + new.objectid_format = :netex + new.save! + new.switch + route = new.routes.new + + route.published_name = route.name = "Route" + route.line = new.line_referential.lines.last + order.each_with_index do |position, i| + _attributes = { + stop_area: stop_areas[i], + position: position + } + route.stop_points.build _attributes + end + route.save + expect(route).to be_valid + expect{route.run_callbacks(:commit)}.to_not raise_error + end + end end diff --git a/spec/policies/merge_policy_spec.rb b/spec/policies/merge_policy_spec.rb new file mode 100644 index 000000000..55d723080 --- /dev/null +++ b/spec/policies/merge_policy_spec.rb @@ -0,0 +1,9 @@ +RSpec.describe MergePolicy, type: :policy do + + let( :record ){ build_stubbed :route } + + permissions :create? do + it_behaves_like 'permitted policy outside referential', 'merges.create' + end + +end |
