diff options
134 files changed, 1178 insertions, 438 deletions
diff --git a/Dockerfile b/Dockerfile index e484de431..17c05110a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ RUN mkdir /app && apt-get update &&\ apt-get clean && apt-get -y autoremove && rm -rf /var/lib/apt/lists/* && \ cd /app && rm config/database.yml && mv config/database.yml.docker config/database.yml && \ cd /app && rm config/secrets.yml && mv config/secrets.yml.docker config/secrets.yml && \ + mkdir -p /app/tmp/imports && \ mv script/launch-cron /app && \ bundle exec whenever --output '/proc/1/fd/1' --update-crontab stif-boiv --set 'environment=production&bundle_command=bundle exec' --roles=app,db,web @@ -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/assets/stylesheets/components/_compliance_control_blocks.sass b/app/assets/stylesheets/components/_compliance_control_blocks.sass new file mode 100644 index 000000000..46880075c --- /dev/null +++ b/app/assets/stylesheets/components/_compliance_control_blocks.sass @@ -0,0 +1,3 @@ +#compliance_control_block_form + .condition-attributes-errors + margin-bottom: 20px diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8b66e6097..7f071a6a4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base # Load helpers in rails engine helper LanguageEngine::Engine.helpers + layout :layout_by_resource def set_locale # I18n.locale = session[:language] || I18n.default_locale @@ -56,4 +57,15 @@ class ApplicationController < ActionController::Base def after_sign_out_path_for(resource_or_scope) new_user_session_path end + + private + + def layout_by_resource + if devise_controller? + "devise" + else + "application" + end + end + end diff --git a/app/controllers/compliance_control_blocks_controller.rb b/app/controllers/compliance_control_blocks_controller.rb index 1173a548a..0851e2800 100644 --- a/app/controllers/compliance_control_blocks_controller.rb +++ b/app/controllers/compliance_control_blocks_controller.rb @@ -4,14 +4,6 @@ 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 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/line_referentials_controller.rb b/app/controllers/line_referentials_controller.rb index 03dab3f8f..e661fbb04 100644 --- a/app/controllers/line_referentials_controller.rb +++ b/app/controllers/line_referentials_controller.rb @@ -2,6 +2,12 @@ class LineReferentialsController < ChouetteController defaults :resource_class => LineReferential + def show + show! do + @line_referential = LineReferentialDecorator.decorate(@line_referential) + end + end + def sync authorize resource, :synchronize? @sync = resource.line_referential_syncs.build diff --git a/app/controllers/lines_controller.rb b/app/controllers/lines_controller.rb index ae8c9ed0c..cd8091252 100644 --- a/app/controllers/lines_controller.rb +++ b/app/controllers/lines_controller.rb @@ -122,7 +122,7 @@ class LinesController < ChouetteController end def line_params - params.require(:line).permit( + out = params.require(:line).permit( :transport_mode, :network_id, :company_id, @@ -148,6 +148,8 @@ class LinesController < ChouetteController :secondary_company_ids => [], footnotes_attributes: [:code, :label, :_destroy, :id] ) + out[:secondary_company_ids] = (out[:secondary_company_ids] || []).select(&:present?) + out end # Fake ransack filter 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/stop_area_referentials_controller.rb b/app/controllers/stop_area_referentials_controller.rb index f2d375e49..0e6a54b49 100644 --- a/app/controllers/stop_area_referentials_controller.rb +++ b/app/controllers/stop_area_referentials_controller.rb @@ -1,6 +1,13 @@ class StopAreaReferentialsController < ChouetteController defaults :resource_class => StopAreaReferential + + def show + show! do + @stop_area_referential = StopAreaReferentialDecorator.decorate(@stop_area_referential) + end + end + def sync authorize resource, :synchronize? @sync = resource.stop_area_referential_syncs.build 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/controllers/vehicle_journeys_collections_controller.rb b/app/controllers/vehicle_journeys_collections_controller.rb index 712bcc154..a117888ab 100644 --- a/app/controllers/vehicle_journeys_collections_controller.rb +++ b/app/controllers/vehicle_journeys_collections_controller.rb @@ -9,8 +9,8 @@ class VehicleJourneysCollectionsController < ChouetteController alias_method :route, :parent def update - state = JSON.parse request.raw_post - Chouette::VehicleJourney.state_update route, state + state = JSON.parse request.raw_post + @resources = Chouette::VehicleJourney.state_update route, state errors = state.any? {|item| item['errors']} respond_to do |format| diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index 220f2d29e..921a2cf7f 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -208,6 +208,7 @@ class VehicleJourneysController < ChouetteController short_id: item.get_objectid.short_id, full_schedule: item.full_schedule?, costs: item.costs, + journey_length: item.journey_length, stop_area_short_descriptions: item.stop_areas.map do |stop| { stop_area_short_description: { 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/decorators/line_referential_decorator.rb b/app/decorators/line_referential_decorator.rb new file mode 100644 index 000000000..1ca0312c3 --- /dev/null +++ b/app/decorators/line_referential_decorator.rb @@ -0,0 +1,13 @@ +class LineReferentialDecorator < AF83::Decorator + decorates LineReferential + + with_instance_decorator do |instance_decorator| + + instance_decorator.action_link policy: :synchronize, primary: :show do |l| + l.content t('actions.sync') + l.href { h. sync_line_referential_path(object.id) } + l.method :post + end + + end +end diff --git a/app/decorators/stop_area_referential_decorator.rb b/app/decorators/stop_area_referential_decorator.rb new file mode 100644 index 000000000..d30501ec9 --- /dev/null +++ b/app/decorators/stop_area_referential_decorator.rb @@ -0,0 +1,13 @@ +class StopAreaReferentialDecorator < AF83::Decorator + decorates StopAreaReferential + + with_instance_decorator do |instance_decorator| + + instance_decorator.action_link policy: :synchronize, primary: :show do |l| + l.content t('actions.sync') + l.href { h. sync_stop_area_referential_path(object.id) } + l.method :post + end + + end +end diff --git a/app/helpers/line_referential_syncs_helper.rb b/app/helpers/line_referential_syncs_helper.rb new file mode 100644 index 000000000..37f08b154 --- /dev/null +++ b/app/helpers/line_referential_syncs_helper.rb @@ -0,0 +1,31 @@ +module LineReferentialSyncsHelper + + def last_line_ref_sync_message(line_ref_sync) + line_ref_sync.line_referential_sync_messages.last + end + + def line_referential_sync_created_at(line_ref_sync) + l(last_line_ref_sync_message(line_ref_sync).created_at, format: :short_with_time) + end + + def line_referential_sync_status(line_ref_sync) + status = line_ref_sync.status + + if %w[new pending].include? status + content_tag :span, '', class: "fa fa-clock-o" + else + cls ='' + cls = 'success' if status == 'successful' + cls = 'danger' if status == 'failed' + + content_tag :span, '', class: "fa fa-circle text-#{cls}" + end + end + + def line_referential_sync_message(line_ref_sync) + last_line_ref_sync_message = last_line_ref_sync_message(line_ref_sync) + data = last_line_ref_sync_message.message_attributes.symbolize_keys! + data[:processing_time] = distance_of_time_in_words(data[:processing_time].to_i) + t("line_referential_sync.message.#{last_line_ref_sync_message.message_key}", last_line_ref_sync_message.message_attributes.symbolize_keys!).html_safe + end +end 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/helpers/stop_area_referential_syncs_helper.rb b/app/helpers/stop_area_referential_syncs_helper.rb new file mode 100644 index 000000000..3e2837fda --- /dev/null +++ b/app/helpers/stop_area_referential_syncs_helper.rb @@ -0,0 +1,31 @@ +module StopAreaReferentialSyncsHelper + + def last_stop_area_ref_sync_message(stop_area_ref_sync) + stop_area_ref_sync.stop_area_referential_sync_messages.last + end + + def stop_area_referential_sync_created_at(stop_area_ref_sync) + l(last_stop_area_ref_sync_message(stop_area_ref_sync).created_at, format: :short_with_time) + end + + def stop_area_referential_sync_status(stop_area_ref_sync) + status = stop_area_ref_sync.status + + if %w[new pending].include? status + content_tag :span, '', class: "fa fa-clock-o" + else + cls ='' + cls = 'success' if status == 'successful' + cls = 'danger' if status == 'failed' + + content_tag :span, '', class: "fa fa-circle text-#{cls}" + end + end + + def stop_area_referential_sync_message(stop_area_ref_sync) + last_stop_area_ref_sync_message = last_stop_area_ref_sync_message(stop_area_ref_sync) + data = last_stop_area_ref_sync_message.message_attributes.symbolize_keys! + data[:processing_time] = distance_of_time_in_words(data[:processing_time].to_i) + t("stop_area_referential_sync.message.#{last_stop_area_ref_sync_message.message_key}", last_stop_area_ref_sync_message.message_attributes.symbolize_keys!).html_safe + end +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/helpers/polyfills.js b/app/javascript/helpers/polyfills.js new file mode 100644 index 000000000..93e3e9846 --- /dev/null +++ b/app/javascript/helpers/polyfills.js @@ -0,0 +1,4 @@ +import 'promise-polyfill/src/polyfill' +import 'es6-symbol/implement' +import 'polyfill-array-includes' +import 'whatwg-fetch' diff --git a/app/javascript/journey_patterns/actions/index.js b/app/javascript/journey_patterns/actions/index.js index ea54f4e05..a813f4b9e 100644 --- a/app/javascript/journey_patterns/actions/index.js +++ b/app/javascript/journey_patterns/actions/index.js @@ -1,10 +1,3 @@ -import Promise from 'promise-polyfill' - -// To add to window -if (!window.Promise) { - window.Promise = Promise; -} - const actions = { enterEditMode: () => ({ type: "ENTER_EDIT_MODE" @@ -195,15 +188,19 @@ const actions = { dispatch(actions.unavailableServer()) } else { if(json.length != 0){ - let val - for (val of json){ - for (let stop_point of val.route_short_description.stop_points){ + let j = 0 + while(j < json.length){ + let val = json[j] + let i = 0 + while(i < val.route_short_description.stop_points.length){ + let stop_point = val.route_short_description.stop_points[i] stop_point.checked = false val.stop_area_short_descriptions.map((element) => { if(element.stop_area_short_description.id === stop_point.id){ stop_point.checked = true } }) + i ++ } journeyPatterns.push( _.assign({}, val, { @@ -212,6 +209,7 @@ const actions = { deletable: false }) ) + j ++ } } window.currentItemsLength = journeyPatterns.length 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/calendars/edit.js b/app/javascript/packs/calendars/edit.js index bd09657ec..0c46313b9 100644 --- a/app/javascript/packs/calendars/edit.js +++ b/app/javascript/packs/calendars/edit.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' diff --git a/app/javascript/packs/exports/new.js b/app/javascript/packs/exports/new.js index ffe702cdb..b113cc709 100644 --- a/app/javascript/packs/exports/new.js +++ b/app/javascript/packs/exports/new.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import MasterSlave from "../../helpers/master_slave" new MasterSlave("form") diff --git a/app/javascript/packs/journey_patterns/index.js b/app/javascript/packs/journey_patterns/index.js index 367a8830f..bd7df2634 100644 --- a/app/javascript/packs/journey_patterns/index.js +++ b/app/javascript/packs/journey_patterns/index.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' @@ -32,7 +34,8 @@ var initialState = { type: '', modalProps: {}, confirmModal: {} - } + }, + custom_fields: window.custom_fields } // const loggerMiddleware = createLogger() diff --git a/app/javascript/packs/referential_lines/show.js b/app/javascript/packs/referential_lines/show.js index 99c5072ef..523f2040f 100644 --- a/app/javascript/packs/referential_lines/show.js +++ b/app/javascript/packs/referential_lines/show.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import clone from '../../helpers/clone' import RoutesMap from '../../helpers/routes_map' diff --git a/app/javascript/packs/referential_overview/overview.js b/app/javascript/packs/referential_overview/overview.js index 59c326e9a..03da12ef3 100644 --- a/app/javascript/packs/referential_overview/overview.js +++ b/app/javascript/packs/referential_overview/overview.js @@ -1 +1,3 @@ +import '../../helpers/polyfills' + import ReferentialOverview from '../../referential_overview' diff --git a/app/javascript/packs/routes/edit.js b/app/javascript/packs/routes/edit.js index fc7aa203d..0512b7aff 100644 --- a/app/javascript/packs/routes/edit.js +++ b/app/javascript/packs/routes/edit.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import PropTypes from 'prop-types' @@ -56,12 +58,25 @@ const getInitialState = () => { } var initialState = { stopPoints: getInitialState() } -const loggerMiddleware = createLogger() -let store = createStore( - reducers, - initialState, - applyMiddleware(thunkMiddleware, promise, loggerMiddleware) -) +let store = null + +if(Object.assign){ + const loggerMiddleware = createLogger() + store = createStore( + reducers, + initialState, + applyMiddleware(thunkMiddleware, promise, loggerMiddleware) + ) +} +else{ + // IE + store = createStore( + reducers, + initialState, + applyMiddleware(thunkMiddleware, promise) + ) +} + render( <Provider store={store}> diff --git a/app/javascript/packs/routes/show.js b/app/javascript/packs/routes/show.js index c20de0800..e8e068ddd 100644 --- a/app/javascript/packs/routes/show.js +++ b/app/javascript/packs/routes/show.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import clone from '../../helpers/clone' import RoutesMap from '../../helpers/routes_map' diff --git a/app/javascript/packs/stop_areas/new.js b/app/javascript/packs/stop_areas/new.js index ffe702cdb..b113cc709 100644 --- a/app/javascript/packs/stop_areas/new.js +++ b/app/javascript/packs/stop_areas/new.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import MasterSlave from "../../helpers/master_slave" new MasterSlave("form") diff --git a/app/javascript/packs/time_tables/edit.js b/app/javascript/packs/time_tables/edit.js index cf058d501..873bdea50 100644 --- a/app/javascript/packs/time_tables/edit.js +++ b/app/javascript/packs/time_tables/edit.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' diff --git a/app/javascript/packs/vehicle_journeys/index.js b/app/javascript/packs/vehicle_journeys/index.js index 9cad3870e..0b4351d26 100644 --- a/app/javascript/packs/vehicle_journeys/index.js +++ b/app/javascript/packs/vehicle_journeys/index.js @@ -1,3 +1,5 @@ +import '../../helpers/polyfills' + import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' diff --git a/app/javascript/routes/actions/index.js b/app/javascript/routes/actions/index.js index 5fbf5bce9..13b2d60b2 100644 --- a/app/javascript/routes/actions/index.js +++ b/app/javascript/routes/actions/index.js @@ -56,12 +56,7 @@ const actions = { unselectMarker: (index) => ({ type: 'UNSELECT_MARKER', index - }), - defaultAttribute: (attribute, stopAreaKind) => { - if (attribute !== '') return attribute - if (stopAreaKind === undefined) return '' - return stopAreaKind === "commercial" ? "normal" : "forbidden" - } + }) } module.exports = actions diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js index 768d069c0..908e97263 100644 --- a/app/javascript/routes/components/StopPoint.js +++ b/app/javascript/routes/components/StopPoint.js @@ -19,14 +19,14 @@ export default function StopPoint(props, {I18n}) { </div> <div> - <select className='form-control' value={defaultAttribute(props.value.for_boarding, props.value.stoparea_kind)} id="for_boarding" onChange={props.onSelectChange}> + <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}> <option value="normal">{I18n.t('routes.edit.stop_point.boarding.normal')}</option> <option value="forbidden">{I18n.t('routes.edit.stop_point.boarding.forbidden')}</option> </select> </div> <div> - <select className='form-control' value={defaultAttribute(props.value.for_alighting, props.value.stoparea_kind)} id="for_alighting" onChange={props.onSelectChange}> + <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}> <option value="normal">{I18n.t('routes.edit.stop_point.alighting.normal')}</option> <option value="forbidden">{I18n.t('routes.edit.stop_point.alighting.forbidden')}</option> </select> diff --git a/app/javascript/routes/index.js b/app/javascript/routes/index.js index 3c7322953..fc99a3086 100644 --- a/app/javascript/routes/index.js +++ b/app/javascript/routes/index.js @@ -22,6 +22,7 @@ const getInitialState = () => { let fancyText = v.name.replace("'", "\'") if(v.zip_code && v.city_name) fancyText += ", " + v.zip_code + " " + v.city_name.replace("'", "\'") + forAlightingAndBoarding = v.stoparea_kind === 'commercial' ? 'normal' : 'forbidden' state.push({ stoppoint_id: v.stoppoint_id, @@ -37,8 +38,8 @@ const getInitialState = () => { name: v.name ? v.name.replace("'", "\'") : '', registration_number: v.registration_number, text: fancyText, - for_boarding: v.for_boarding || '', - for_alighting: v.for_alighting || '', + for_boarding: v.for_boarding || forAlightingAndBoarding, + for_alighting: v.for_alighting || forAlightingAndBoarding, longitude: v.longitude || 0, latitude: v.latitude || 0, comment: v.comment ? v.comment.replace("'", "\'") : '', diff --git a/app/javascript/routes/reducers/stopPoints.js b/app/javascript/routes/reducers/stopPoints.js index ba183d002..54142338d 100644 --- a/app/javascript/routes/reducers/stopPoints.js +++ b/app/javascript/routes/reducers/stopPoints.js @@ -8,8 +8,8 @@ const stopPoint = (state = {}, action, length) => { text: '', index: length, edit: true, - for_boarding: '', - for_alighting: '', + for_boarding: 'normal', + for_alighting: 'normal', olMap: { isOpened: false, json: {} @@ -61,6 +61,7 @@ const stopPoints = (state = [], action) => { case 'UPDATE_INPUT_VALUE': return state.map( (t, i) => { if (i === action.index) { + let forAlightingAndBoarding = action.text.stoparea_kind === 'commercial' ? 'normal' : 'forbidden' return _.assign( {}, t, @@ -77,7 +78,9 @@ const stopPoints = (state = [], action) => { area_type: action.text.area_type, city_name: action.text.city_name, comment: action.text.comment, - registration_number: action.text.registration_number + registration_number: action.text.registration_number, + for_alighting: forAlightingAndBoarding, + for_boarding: forAlightingAndBoarding } ) } else { diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js index 7c79dfe52..a5c454a18 100644 --- a/app/javascript/time_tables/actions/index.js +++ b/app/javascript/time_tables/actions/index.js @@ -191,10 +191,13 @@ const actions = { isInPeriod: (periods, date) => { date = new Date(date) - for (let period of periods) { + let i = 0; + while(i < periods.length){ + let period = periods[i] let begin = new Date(period.period_start) let end = new Date(period.period_end) if (date >= begin && date <= end) return true + i++ } return false @@ -235,12 +238,14 @@ const actions = { let error = '' start = new Date(start) end = new Date(end) - - for (let day of in_days) { + let i = 0; + while(i < in_days){ + let day = in_days[i] if (start <= new Date(day.date) && end >= new Date(day.date)) { error = I18n.t('time_tables.edit.error_submit.dates_overlaps') break } + i ++ } return error }, 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/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index 537dcfc06..70d6e953a 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -1,9 +1,3 @@ -import Promise from 'promise-polyfill' - -// To add to window -if (!window.Promise) { - window.Promise = Promise; -} import { batchActions } from '../batch' const actions = { @@ -54,16 +48,10 @@ const actions = { }), selectJPCreateModal : (selectedJP) => ({ type : 'SELECT_JP_CREATE_MODAL', - selectedItem: { - id: selectedJP.id, + selectedItem: _.assign({}, selectedJP, { objectid: selectedJP.object_id, - short_id: selectedJP.short_id, - name: selectedJP.name, - published_name: selectedJP.published_name, - stop_areas: selectedJP.stop_area_short_descriptions, - costs: selectedJP.costs, - full_schedule: selectedJP.full_schedule - } + stop_areas: selectedJP.stop_area_short_descriptions + }) }), openEditModal : (vehicleJourney) => ({ type : 'EDIT_VEHICLEJOURNEY_MODAL', @@ -339,16 +327,23 @@ const actions = { if(hasError == true) { dispatch(actions.unavailableServer()) } else { - let val - for (val of json.vehicle_journeys){ + let i = 0 + while(i < json.vehicle_journeys.length){ + let val = json.vehicle_journeys[i] + i++ var timeTables = [] var purchaseWindows = [] - let tt - for (tt of val.time_tables){ + let k = 0 + while(k < val.time_tables.length){ + let tt = val.time_tables[k] + k++ timeTables.push(tt) } if(val.purchase_windows){ - for (tt of val.purchase_windows){ + k = 0 + while(k < val.purchase_windows.length){ + let tt = val.purchase_windows[k] + k++ purchaseWindows.push(tt) } } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index 73d99d120..5eb73de0e 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -10,6 +10,10 @@ export default class VehicleJourney extends Component { this.previousCity = undefined } + journey_length() { + return this.props.value.journey_pattern.journey_length + "km" + } + cityNameChecker(sp) { return this.props.vehicleJourneys.showHeader(sp.stop_point_objectid) } @@ -115,6 +119,11 @@ export default class VehicleJourney extends Component { <div key={i}>{this.extraHeaderValue(header)}</div> ) } + { this.hasFeature('journey_length_in_vehicle_journeys') && + <div> + {this.journey_length()} + </div> + } { this.hasFeature('purchase_windows') && <div> {purchase_windows.slice(0,3).map((tt, i)=> diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index c6f59ce9d..27e147b37 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'> @@ -222,6 +224,11 @@ export default class VehicleJourneys extends Component { <div key={i}>{this.extraHeaderLabel(header)}</div> ) } + { this.hasFeature('journey_length_in_vehicle_journeys') && + <div> + {I18n.attribute_name("vehicle_journey", "journey_length")} + </div> + } { this.hasFeature('purchase_windows') && <div> { detailed_purchase_windows && 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/calendar/period.rb b/app/models/calendar/period.rb index 07926e818..c549a7575 100644 --- a/app/models/calendar/period.rb +++ b/app/models/calendar/period.rb @@ -16,7 +16,7 @@ class Calendar < ApplicationModel alias_method :period_end=, :end= def check_end_greather_than_begin - if self.begin && self.end && self.begin >= self.end + if self.begin && self.end && self.begin > self.end errors.add(:base, I18n.t('calendars.errors.short_period')) end end diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb index 830a6a808..4b4cc2c73 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 @@ -78,10 +86,8 @@ module Chouette def state_stop_points_update item item['stop_points'].each do |sp| - exist = stop_area_ids.include?(sp['id']) - next if exist && sp['checked'] - - stop_point = route.stop_points.find_by(stop_area_id: sp['id']) + stop_point = route.stop_points.find_by(stop_area_id: sp['id'], position: sp['position']) + exist = stop_points.include?(stop_point) if !exist && sp['checked'] stop_points << stop_point end @@ -161,18 +167,18 @@ module Chouette next finish unless start.present? costs = costs_between(start, finish) full = false unless costs.present? - full = false unless costs[:distance] && costs[:distance] > 0 full = false unless costs[:time] && costs[:time] > 0 finish end full end - def distance_to stop + def distance_between start, stop + return 0 unless start.position < stop.position val = 0 - i = 0 - _end = stop_points.first - while _end != stop + i = stop_points.index(start) + _end = start + while _end && _end != stop i += 1 _start = _end _end = stop_points[i] @@ -181,6 +187,28 @@ module Chouette val end + def distance_to stop + distance_between stop_points.first, stop + end + + def journey_length + i = 0 + j = stop_points.length - 1 + start = stop_points[i] + stop = stop_points[j] + while i < j && start.kind == "non_commercial" + i+= 1 + start = stop_points[i] + end + + while i < j && stop.kind == "non_commercial" + j-= 1 + stop = stop_points[j] + end + return 0 unless start && stop + distance_between start, stop + end + def set_distances distances raise "inconsistent data: #{distances.count} values for #{stop_points.count} stops" unless distances.count == stop_points.count prev = distances[0].to_i 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 9c7a3e6d9..a5eab3002 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -7,6 +7,16 @@ module Chouette include ObjectidSupport extend Enumerize + 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 in Route #{id} : #{positions.inspect}" + end + end + end + 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 @@ -68,8 +78,14 @@ 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 = { 'opposite_route_id' => nil, 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/stop_point.rb b/app/models/chouette/stop_point.rb index 1df1a664a..6f2d89578 100644 --- a/app/models/chouette/stop_point.rb +++ b/app/models/chouette/stop_point.rb @@ -9,7 +9,6 @@ module Chouette include ForAlightingEnumerations include ObjectidSupport - belongs_to :stop_area belongs_to :route, inverse_of: :stop_points has_many :vehicle_journey_at_stops, :dependent => :destroy @@ -17,7 +16,6 @@ module Chouette acts_as_list :scope => :route, top_of_list: 0 - validates_presence_of :stop_area validate :stop_area_id_validation def stop_area_id_validation @@ -28,7 +26,7 @@ module Chouette scope :default_order, -> { order("position") } - delegate :name, to: :stop_area + delegate :name, :kind, :area_type, to: :stop_area before_destroy :remove_dependent_journey_pattern_stop_points def remove_dependent_journey_pattern_stop_points 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/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 54aad290c..c269d478e 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -244,11 +244,13 @@ module Chouette end def self.state_update route, state + objects = [] transaction do state.each do |item| item.delete('errors') vj = find_by(objectid: item['objectid']) || state_create_instance(route, item) next if item['deletable'] && vj.persisted? && vj.destroy + objects << vj if vj.state_update_vjas?(item['vehicle_journey_at_stops']) vj.update_vjas_from_state(item['vehicle_journey_at_stops']) @@ -276,6 +278,7 @@ module Chouette item['vehicle_journey_at_stops'].map {|vjas| vjas.delete('new_record') } end state.delete_if {|item| item['deletable']} + objects end def self.state_create_instance route, item diff --git a/app/models/concerns/checksum_support.rb b/app/models/concerns/checksum_support.rb index 92103798e..de3a6e16b 100644 --- a/app/models/concerns/checksum_support.rb +++ b/app/models/concerns/checksum_support.rb @@ -40,6 +40,7 @@ module ChecksumSupport def current_checksum_source source = checksum_replace_nil_or_empty_values(self.checksum_attributes) + source += self.custom_fields_checksum if self.respond_to?(:custom_fields_checksum) source.map{ |item| if item.kind_of?(Array) item.map{ |x| x.kind_of?(Array) ? "(#{x.join(',')})" : x }.join(',') diff --git a/app/models/concerns/custom_fields_support.rb b/app/models/concerns/custom_fields_support.rb index 46fc8e73d..c39dfd1fc 100644 --- a/app/models/concerns/custom_fields_support.rb +++ b/app/models/concerns/custom_fields_support.rb @@ -28,6 +28,10 @@ module CustomFieldsSupport CustomField::Collection.new self, workgroup end + def custom_fields_checksum + custom_fields.values.map(&:checksum) + end + def custom_field_values= vals out = {} custom_fields.each do |code, field| diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 22118a15a..88783b5b4 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| @@ -67,6 +63,10 @@ class CustomField < ApplicationModel @raw_value end + def checksum + @raw_value + end + def input form_helper @input ||= begin klass_name = field_type && "CustomField::Instance::#{field_type.classify}::Input" @@ -191,9 +191,10 @@ class CustomField < ApplicationModel custom_field_code = self.code _attr_name = attr_name _uploader_name = uploader_name + _digest_name = digest_name owner.send :define_singleton_method, "read_uploader" do |attr| if attr.to_s == _attr_name - custom_field_values[custom_field_code] + custom_field_values[custom_field_code] && custom_field_values[custom_field_code]["path"] else read_attribute attr end @@ -201,16 +202,28 @@ class CustomField < ApplicationModel owner.send :define_singleton_method, "write_uploader" do |attr, val| if attr.to_s == _attr_name - custom_field_values[custom_field_code] = val + self.custom_field_values[custom_field_code] ||= {} + self.custom_field_values[custom_field_code]["path"] = val + self.custom_field_values[custom_field_code]["digest"] = self.send _digest_name else write_attribute attr, val end end owner.send :define_singleton_method, "#{_attr_name}_will_change!" do + self.send "#{_digest_name}=", nil custom_field_values_will_change! end + owner.send :define_singleton_method, _digest_name do + val = instance_variable_get "@#{_digest_name}" + if val.nil? && (file = send(_uploader_name)).present? + val = CustomField::Instance::Attachment.digest(file) + instance_variable_set "@#{_digest_name}", val + end + val + end + _extension_whitelist = options["extension_whitelist"] owner.send :define_singleton_method, "#{_uploader_name}_extension_whitelist" do @@ -219,7 +232,15 @@ class CustomField < ApplicationModel unless owner.class.uploaders.has_key? _uploader_name.to_sym owner.class.mount_uploader _uploader_name, CustomFieldAttachmentUploader, mount_on: "custom_field_#{code}_raw_value" + owner.class.send :attr_accessor, _digest_name end + + digest = @raw_value && @raw_value["digest"] + owner.send "#{_digest_name}=", digest + end + + def self.digest file + Digest::SHA256.file(file.path).hexdigest end def preprocess_value_for_assignment val @@ -230,6 +251,10 @@ class CustomField < ApplicationModel end end + def checksum + owner.send digest_name + end + def value owner.send "custom_field_#{code}" end @@ -246,6 +271,10 @@ class CustomField < ApplicationModel "custom_field_#{code}" end + def digest_name + "#{uploader_name}_digest" + end + def display_value render_partial end 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/models/referential.rb b/app/models/referential.rb index 1794126a2..78b719fab 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -78,31 +78,29 @@ class Referential < ApplicationModel alias_method_chain :save, :table_lock_timeout - if Rails.env.development? - def self.force_register_models_with_checksum - paths = Rails.application.paths['app/models'].to_a - Rails.application.railties.each do |tie| - next unless tie.respond_to? :paths - paths += tie.paths['app/models'].to_a - end + def self.force_register_models_with_checksum + paths = Rails.application.paths['app/models'].to_a + Rails.application.railties.each do |tie| + next unless tie.respond_to? :paths + paths += tie.paths['app/models'].to_a + end - paths.each do |path| - next unless File.directory?(path) - Dir.chdir path do - Dir['**/*.rb'].each do |src| - next if src =~ /^concerns/ - # thanks for inconsistent naming ... - if src == "route_control/zdl_stop_area.rb" - RouteControl::ZDLStopArea - next - end - Rails.logger.info "Loading #{src}" - begin - src[0..-4].classify.safe_constantize - rescue => e - Rails.logger.info "Failed: #{e.message}" - nil - end + paths.each do |path| + next unless File.directory?(path) + Dir.chdir path do + Dir['**/*.rb'].each do |src| + next if src =~ /^concerns/ + # thanks for inconsistent naming ... + if src == "route_control/zdl_stop_area.rb" + RouteControl::ZDLStopArea + next + end + Rails.logger.info "Loading #{src}" + begin + src[0..-4].classify.safe_constantize + rescue => e + Rails.logger.info "Failed: #{e.message}" + nil end end 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/compliance_control_blocks/_form.html.slim b/app/views/compliance_control_blocks/_form.html.slim index 2e87a877e..e8ae63384 100644 --- a/app/views/compliance_control_blocks/_form.html.slim +++ b/app/views/compliance_control_blocks/_form.html.slim @@ -1,6 +1,12 @@ = simple_form_for [@compliance_control_set, @compliance_control_block], html: { class: 'form-horizontal', id: 'compliance_control_block_form' }, wrapper: :horizontal_form do |f| .row .col-lg-12 + - if @compliance_control_block.errors.has_key? :condition_attributes + .row.condition-attributes-errors + .col-lg-12 + .alert.alert-danger + - @compliance_control_block.errors[:condition_attributes].each do |msg| + p.small = "- #{msg}" .form-group = f.input :transport_mode, as: :select, collection: ComplianceControlBlock.sorted_transport_modes, label: t('activerecord.attributes.compliance_control_blocks.transport_mode'), label_method: lambda {|t| ("<span>" + t("enumerize.transport_mode.#{t}") + "</span>").html_safe } = f.input :transport_submode, as: :select, collection: ComplianceControlBlock.sorted_transport_submodes, label: t('activerecord.attributes.compliance_control_blocks.transport_submode'), label_method: lambda {|t| ("<span>" + t("enumerize.transport_submode.#{t}") + "</span>").html_safe } 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/layouts/application.html.slim b/app/views/layouts/application.html.slim index 3921c8701..abf39c089 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -17,7 +17,11 @@ html lang=I18n.locale | I18n.locale = '#{I18n.locale}' body - = render 'layouts/navigation/main_nav' + nav#main_nav + // Left menu content + = render 'layouts/navigation/main_nav_left' + // Top menu content + = render 'layouts/navigation/main_nav_top' = render 'layouts/flash_messages', flash: flash = render 'layouts/navigation/page_header' = yield diff --git a/app/views/layouts/devise.html.slim b/app/views/layouts/devise.html.slim new file mode 100644 index 000000000..34a3a3f11 --- /dev/null +++ b/app/views/layouts/devise.html.slim @@ -0,0 +1,28 @@ +doctype html +html lang=I18n.locale + head + meta charset="utf-8" + meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" + + = csrf_meta_tag + + title = t('brandname') + + = stylesheet_link_tag 'base' + = stylesheet_link_tag 'application' + + = javascript_pack_tag 'application' + = javascript_include_tag 'application' + = javascript_tag do + | I18n.locale = '#{I18n.locale}' + + body + nav#main_nav + // Top menu content + = render 'layouts/navigation/main_nav_top' + = render 'layouts/flash_messages', flash: flash + = render 'layouts/navigation/page_header' + = yield + + = render 'shared/development_toolbar' + = yield :javascript diff --git a/app/views/layouts/navigation/_main_nav.html.slim b/app/views/layouts/navigation/_main_nav.html.slim deleted file mode 100644 index 806290223..000000000 --- a/app/views/layouts/navigation/_main_nav.html.slim +++ /dev/null @@ -1,6 +0,0 @@ -nav#main_nav - // Left menu content - = render 'layouts/navigation/main_nav_left' - - // Top menu content - = render 'layouts/navigation/main_nav_top' diff --git a/app/views/layouts/navigation/_main_nav_left_content.html.slim b/app/views/layouts/navigation/_main_nav_left_content.html.slim index e69de29bb..0b55578a7 100644 --- a/app/views/layouts/navigation/_main_nav_left_content.html.slim +++ b/app/views/layouts/navigation/_main_nav_left_content.html.slim @@ -0,0 +1,64 @@ + +- current_organisation.workbenches.each do |workbench| + #menu-items.panel-group + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miOne', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.current_offer.other') + + #miOne.panel-collapse.collapse + .list-group + = link_to root_path, class: "list-group-item" do + span = t('layouts.navbar.dashboard') + = link_to workbench_output_path(workbench), class: 'list-group-item' do + span = t('layouts.navbar.workbench_outputs.organisation') + = link_to '#', class: 'list-group-item disabled' do + span = t('layouts.navbar.workbench_outputs.workgroup') + + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miTwo', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + - t('activerecord.models.workbench.one').capitalize + + #miTwo.panel-collapse.collapse + .list-group + = link_to workbench_path(workbench), class: "list-group-item" do + span = t('activerecord.models.referential.other').capitalize + = link_to workbench_imports_path(workbench), class: "list-group-item" do + span = t('activerecord.models.import.other').capitalize + = link_to workbench_exports_path(workbench), class: "list-group-item" do + span = t('activerecord.models.export.other').capitalize + = link_to workgroup_calendars_path(workbench.workgroup), class: 'list-group-item' do + span = t('activerecord.models.calendar.other').capitalize + = link_to workbench_compliance_check_sets_path(workbench), class: 'list-group-item' do + span = t('activerecord.models.compliance_check_set.other').capitalize + = link_to compliance_control_sets_path, class: 'list-group-item' do + span = t('activerecord.models.compliance_control_set.other').capitalize + + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miFour', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.line_referential') + + #miFour.panel-collapse.collapse + .list-group + = link_to line_referential_lines_path(workbench.line_referential), class: "list-group-item" do + span = Chouette::Line.t.capitalize + = link_to line_referential_networks_path(workbench.line_referential), class: "list-group-item" do + span = Chouette::Network.t.capitalize + = link_to line_referential_companies_path(workbench.line_referential), class: "list-group-item" do + span = Chouette::Company.t.capitalize + + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miFive', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.stop_area_referential') + + #miFive.panel-collapse.collapse + .list-group + = link_to stop_area_referential_stop_areas_path(workbench.stop_area_referential), class: "list-group-item" do + span = Chouette::StopArea.t.capitalize diff --git a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim index 02614dcab..9404eeae6 100644 --- a/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim +++ b/app/views/layouts/navigation/_main_nav_left_content_stif.html.slim @@ -1,109 +1,95 @@ -- @localizationUrl = "#{params[:controller]}##{params[:action]}" - -#menu-items.panel-group - .menu-item.panel - .panel-heading - h4.panel-title - = link_to '#miOne', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do - |Offres courantes - - #miOne.panel-collapse.collapse - .list-group - = link_to root_path, class: "list-group-item #{(@localizationUrl == 'workbenches#index') ? 'active' : ''}" do - span Tableau de bord - = link_to '#', class: 'list-group-item' do - span Offre de mon organisation - = link_to '#', class: 'list-group-item' do - span Offre IDF - - .menu-item.panel - .panel-heading - h4.panel-title - = link_to '#miTwo', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do - |Espace de travail - - #miTwo.panel-collapse.collapse - .list-group - - current_user.workbenches.each do |current_workbench| - = link_to workbench_path(current_workbench), class: "list-group-item #{params[:controller] == 'workbenches' ? 'active' : ''}" do - span Jeux de données - = link_to workbench_imports_path(current_workbench), class: "list-group-item #{(params[:controller] == 'imports') ? 'active' : ''}" do - span Import - = link_to workbench_exports_path(current_workbench), class: "list-group-item #{(params[:controller] == 'exports') ? 'active' : ''}" do - span Export - = link_to workgroup_calendars_path(current_workbench.workgroup), class: 'list-group-item' do - span Modèles de calendrier - = link_to workbench_compliance_check_sets_path(current_workbench), class: 'list-group-item' do - span Rapport de contrôle +- current_organisation.workbenches.each do |workbench| + #menu-items.panel-group + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miOne', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.current_offer.other') + + #miOne.panel-collapse.collapse + .list-group + = link_to root_path, class: "list-group-item" do + span = t('layouts.navbar.dashboard') + = link_to workbench_output_path(workbench), class: 'list-group-item' do + span = t('layouts.navbar.workbench_outputs.organisation') + = link_to '#', class: 'list-group-item disabled' do + span = t('layouts.navbar.workbench_outputs.workgroup') + + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miTwo', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + - t('activerecord.models.workbench.one').capitalize + + #miTwo.panel-collapse.collapse + .list-group + = link_to workbench_path(workbench), class: "list-group-item" do + span = t('activerecord.models.referential.other').capitalize + = link_to workbench_imports_path(workbench), class: "list-group-item" do + span = t('activerecord.models.import.other').capitalize + = link_to workbench_exports_path(workbench), class: "list-group-item" do + span = t('activerecord.models.export.other').capitalize + = link_to workgroup_calendars_path(workbench.workgroup), class: 'list-group-item' do + span = t('activerecord.models.calendar.other').capitalize + = link_to workbench_compliance_check_sets_path(workbench), class: 'list-group-item' do + span = t('activerecord.models.compliance_check_set.other').capitalize = link_to compliance_control_sets_path, class: 'list-group-item' do - span Jeux de contrôle + span = t('activerecord.models.compliance_control_set.other').capitalize - .menu-item.panel - .panel-heading - h4.panel-title - = link_to '#miThree', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do - |Données + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miFour', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.line_referential') - #miThree.panel-collapse.collapse - - if @referential.try(:id) && respond_to?(:current_referential) + #miFour.panel-collapse.collapse .list-group - .list-group-item - = (current_referential.name).upcase - .list-group - = link_to referential_networks_path(current_referential), class: 'list-group-item' do - span = t('networks.index.title') - - = link_to referential_companies_path(current_referential), class: 'list-group-item' do - span = t('companies.index.title') - - = link_to '#', class: 'list-group-item disabled' do - span Tracés - - = link_to referential_time_tables_path(current_referential), class: 'list-group-item' do - span = t('time_tables.index.title') - - - else - .panel-body - em.text-muted - = "Sélectionnez un jeu de données pour accéder à plus de fonctionnalités" - - .menu-item.panel - .panel-heading - h4.panel-title - = link_to '#miFour', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do - |Synchronisation - - #miFour.panel-collapse.collapse - .list-group - = link_to line_referential_path(1), class: "list-group-item #{(@localizationUrl == 'line_referentials#show') ? 'active' : ''}" do - span Synchronisation iLICO - = link_to stop_area_referential_path(1), class: "list-group-item #{(@localizationUrl == 'stop_area_referentials#show') ? 'active' : ''}" do - span Synchronisation iCAR - - .menu-item.panel - .panel-heading - h4.panel-title - = link_to '#miFive', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do - |Outils - - #miFive.panel-collapse.collapse - .list-group - = link_to Rails.application.config.try(:portal_url), target: '_blank', class: 'list-group-item' do - span - span.fa.fa-2x.fa-circle - |Portail (POSTIF) - - = link_to Rails.application.config.try(:codifligne_url), target: '_blank', class: 'list-group-item' do - span - span.fa.fa-2x.fa-circle - |iLICO - - = link_to Rails.application.config.try(:reflex_url), target: '_blank', class: 'list-group-item' do - span - span.fa.fa-2x.fa-circle - |iCAR - - = link_to '#', target: '_blank', class: 'list-group-item' do - span - span.fa.fa-2x.fa-circle - |Support + = link_to line_referential_path(workbench.line_referential), class: "list-group-item" do + span = t('layouts.navbar.sync_ilico') + = link_to line_referential_lines_path(workbench.line_referential), class: "list-group-item" do + span = Chouette::Line.t.capitalize + = link_to line_referential_networks_path(workbench.line_referential), class: "list-group-item" do + span = Chouette::Network.t.capitalize + = link_to line_referential_companies_path(workbench.line_referential), class: "list-group-item" do + span = Chouette::Company.t.capitalize + + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miFive', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.stop_area_referential') + + #miFive.panel-collapse.collapse + .list-group + = link_to stop_area_referential_path(workbench.stop_area_referential), class: "list-group-item" do + span = t('layouts.navbar.sync_icar') + = link_to stop_area_referential_stop_areas_path(workbench.stop_area_referential), class: "list-group-item" do + span = Chouette::StopArea.t.capitalize + + .menu-item.panel + .panel-heading + h4.panel-title + = link_to '#miSix', data: {toggle: 'collapse', parent: '#menu-items'}, 'aria-expanded' => 'false' do + = t('layouts.navbar.tools') + + #miSix.panel-collapse.collapse + .list-group + = link_to Rails.application.config.try(:portal_url), target: '_blank', class: 'list-group-item' do + span + span.fa.fa-2x.fa-circle + = t('layouts.navbar.portal') + + = link_to Rails.application.config.try(:codifligne_url), target: '_blank', class: 'list-group-item' do + span + span.fa.fa-2x.fa-circle + = t('layouts.navbar.ilico') + + = link_to Rails.application.config.try(:reflex_url), target: '_blank', class: 'list-group-item' do + span + span.fa.fa-2x.fa-circle + = t('layouts.navbar.icar') + + = link_to '#', target: '_blank', class: 'list-group-item' do + span + span.fa.fa-2x.fa-circle + = t('layouts.navbar.support') diff --git a/app/views/line_referentials/show.html.slim b/app/views/line_referentials/show.html.slim index 763eb076e..1f184b6f2 100644 --- a/app/views/line_referentials/show.html.slim +++ b/app/views/line_referentials/show.html.slim @@ -1,47 +1,29 @@ - breadcrumb :line_referential, @line_referential - page_header_content_for @line_referential -- if policy(@line_referential).synchronize? - - content_for :page_header_actions do - = link_to(t('actions.sync'), sync_line_referential_path(@line_referential), method: :post, class: 'btn btn-default') - -- content_for :page_header_content do - .row.mb-md - .col-lg-12.text-right - = link_to line_referential_companies_path(@line_referential), class: 'btn btn-primary' do - = Referential.human_attribute_name(:companies) - em.small = " (#{@line_referential.companies.size})" - = link_to line_referential_networks_path(@line_referential), class: 'btn btn-primary' do - = Referential.human_attribute_name(:networks) - em.small = " (#{@line_referential.networks.size})" - = link_to line_referential_lines_path(@line_referential), class: 'btn btn-primary' do - = Referential.human_attribute_name(:lines) - em.small = " (#{@line_referential.lines.size})" .page_content .container-fluid .row .col-lg-12 - - unless @line_referential.line_referential_syncs.empty? - table.table - thead - tr - th = t('.synchronized') - th = t('.status') - th = t('.message') + = table_builder_2 @line_referential.line_referential_syncs, + [ \ + TableBuilderHelper::Column.new( \ + name: t('.synchronized'), \ + attribute: Proc.new { |sync| line_referential_sync_created_at(sync) }, \ + ), \ + TableBuilderHelper::Column.new( \ + name: t('.status'), \ + attribute: Proc.new { |sync| line_referential_sync_status(sync) }, \ + ), \ + TableBuilderHelper::Column.new( \ + name: t('.message'), \ + attribute: Proc.new { |sync| line_referential_sync_message(sync) }, \ + ), \ + ], + sortable: false, + cls: 'table' - tbody - - @line_referential.line_referential_syncs.each_with_index do |sync, i| - / Display only 10 msgs - - if i < 10 - - unless sync.line_referential_sync_messages.empty? - - sync.line_referential_sync_messages.last.tap do |log| - - if log.criticity = log.criticity - tr - td style='width: 150px' - = l(log.created_at, format: :short_with_time) - td.text-center - .fa.fa-circle class="text-#{criticity_class(log.criticity)}" - td - - data = log.message_attributes.symbolize_keys! - - data[:processing_time] = distance_of_time_in_words(data[:processing_time].to_i) - = t("line_referential_sync.message.#{log.message_key}", log.message_attributes.symbolize_keys!).html_safe + - unless @line_referential.line_referential_syncs.any? + .row.mt-xs + .col-lg-12 + = replacement_msg t('line_referential_syncs.search_no_results') diff --git a/app/views/lines/index.html.slim b/app/views/lines/index.html.slim index 9d491ace4..4d4ba938d 100644 --- a/app/views/lines/index.html.slim +++ b/app/views/lines/index.html.slim @@ -57,4 +57,4 @@ - unless @lines.any? .row.mt-xs .col-lg-12 - = replacement_msg t('referential_lines.search_no_results') + = replacement_msg 'referential_lines.search_no_results'.t 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_companies/edit.html.slim b/app/views/referential_companies/edit.html.slim index 95be64aa1..0c9fb1f87 100644 --- a/app/views/referential_companies/edit.html.slim +++ b/app/views/referential_companies/edit.html.slim @@ -1,5 +1,8 @@ - breadcrumb :referential_company, @referential, @company - page_header_content_for @company + .page_content .container-fluid - = render 'form' + .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_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/stif/dashboards/_dashboard.html.slim b/app/views/stif/dashboards/_dashboard.html.slim index 7538c7fc7..74e607e26 100644 --- a/app/views/stif/dashboards/_dashboard.html.slim +++ b/app/views/stif/dashboards/_dashboard.html.slim @@ -5,8 +5,13 @@ h3.panel-title = t('.organisation') - .panel-body - em.small.text-muted = t('.no_content') + - if @dashboard.workbench.output.referentials.present? + - @dashboard.workbench.output.referentials.first(5).each do |referential| + .list-group + = link_to referential.name, referential_path(referential), class: 'list-group-item' + - else + .panel-body + em.small.text-muted = t('.no_content') .panel.panel-default .panel-heading diff --git a/app/views/stop_area_referentials/show.html.slim b/app/views/stop_area_referentials/show.html.slim index 911006c39..bca89a0f4 100644 --- a/app/views/stop_area_referentials/show.html.slim +++ b/app/views/stop_area_referentials/show.html.slim @@ -1,41 +1,29 @@ - breadcrumb :stop_area_referential, @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') - -- content_for :page_header_content do - .row.mb-md - .col-lg-12.text-right - = link_to stop_area_referential_stop_areas_path(@stop_area_referential), class: 'btn btn-primary' do - = Referential.human_attribute_name(:stop_areas) - em.small = " (#{@stop_area_referential.stop_areas.count})" - page_header_content_for @stop_area_referential .page_content .container-fluid .row .col-lg-12 - - unless @stop_area_referential.stop_area_referential_syncs.empty? - table.table - thead - tr - th Synchronisé - th Statut - th Message + = table_builder_2 @stop_area_referential.stop_area_referential_syncs, + [ \ + TableBuilderHelper::Column.new( \ + name: t('.synchronized'), \ + attribute: Proc.new { |sync| stop_area_referential_sync_created_at(sync) }, \ + ), \ + TableBuilderHelper::Column.new( \ + name: t('.status'), \ + attribute: Proc.new { |sync| stop_area_referential_sync_status(sync) }, \ + ), \ + TableBuilderHelper::Column.new( \ + name: t('.message'), \ + attribute: Proc.new { |sync| stop_area_referential_sync_message(sync) }, \ + ), \ + ], + sortable: false, + cls: 'table' - tbody - - @stop_area_referential.stop_area_referential_syncs.each_with_index do |sync, i| - / Display only 10 msgs - - if i < 10 - - unless sync.stop_area_referential_sync_messages.empty? - - sync.stop_area_referential_sync_messages.last.tap do |log| - - if log.criticity = log.criticity - tr - td style='width:150px' - = l(log.created_at, format: :short_with_time) - td.text-center - .fa.fa-circle class="text-#{criticity_class(log.criticity)}" - td - - data = log.message_attributes.symbolize_keys! - - data[:processing_time] = distance_of_time_in_words(data[:processing_time].to_i) - = t("stop_area_referential_sync.message.#{log.message_key}", log.message_attributes.symbolize_keys!).html_safe + - unless @stop_area_referential.stop_area_referential_syncs.any? + .row.mt-xs + .col-lg-12 + = replacement_msg t('stop_area_referential_syncs.search_no_results') diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index d218038a6..6c588416c 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -17,7 +17,7 @@ child(:route) do |route| end child(:journey_pattern) do |journey_pattern| - attributes :id, :objectid, :name, :published_name + attributes :id, :objectid, :name, :published_name, :journey_length node(:short_id) {journey_pattern.get_objectid.short_id} end 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/breadcrumbs.rb b/config/breadcrumbs.rb index e57cbc4f2..e60ff187f 100644 --- a/config/breadcrumbs.rb +++ b/config/breadcrumbs.rb @@ -169,7 +169,6 @@ end crumb :companies do |line_referential| link I18n.t('companies.index.title'), line_referential_companies_path(line_referential) - parent :line_referential, line_referential end crumb :company do |company| @@ -179,7 +178,6 @@ end crumb :networks do |line_referential| link I18n.t('networks.index.title'), line_referential_networks_path(line_referential) - parent :line_referential, line_referential end crumb :network do |network| @@ -189,7 +187,6 @@ end crumb :group_of_lines do |line_referential| link I18n.t('group_of_lines.index.title'), line_referential_group_of_lines_path(line_referential) - parent :line_referential, line_referential end crumb :group_of_line do |group_of_line| @@ -199,7 +196,6 @@ end crumb :lines do |line_referential| link I18n.t('lines.index.title'), line_referential_lines_path - parent :line_referential, line_referential end crumb :line do |line| diff --git a/config/locales/dashboard.en.yml b/config/locales/dashboard.en.yml index 361a3cf2b..472f5bc2f 100644 --- a/config/locales/dashboard.en.yml +++ b/config/locales/dashboard.en.yml @@ -1,5 +1,6 @@ en: dashboards: + main_nav_left: Dashboard show: title: "Dashboard %{organisation}" workbench: diff --git a/config/locales/dashboard.fr.yml b/config/locales/dashboard.fr.yml index 1e1c095b1..65022fcb8 100644 --- a/config/locales/dashboard.fr.yml +++ b/config/locales/dashboard.fr.yml @@ -1,5 +1,6 @@ fr: dashboards: + main_nav_left: Tableau de bord show: title: "Tableau de bord %{organisation}" workbench: diff --git a/config/locales/layouts.en.yml b/config/locales/layouts.en.yml index d5717b400..31bff403c 100644 --- a/config/locales/layouts.en.yml +++ b/config/locales/layouts.en.yml @@ -10,9 +10,28 @@ en: navbar: return_to_referentials: "Return to data spaces" select_referential: "Select data space" + select_referential_for_more_features: "Select data space for more foeatures" select_referential_datas: "Select datas" return_to_dashboard: "Return to Dashboard" + dashboard: Dashboard referential_datas: "Datas" + line_referential: "Line referential" + stop_area_referential: "Stop area referential" + current_offer: + one: Current offer + other: Current offers + workbench_output: + organisation: Organisation offers + workgroup: Workgroup offers + tools: Tools + sync: Synchronization + sync_icar: iCAR synchronization + sync_ilico: iLICO synchronization + icar: iCAR + ilico: iLICO + portal: Portal (POSTIF) + support: Support + shapes: Shapes history_tag: title: "Metadatas" created_at: "Created at" @@ -51,4 +70,4 @@ en: attributes: author: "Edited by" created_at: "Created at" - updated_at: "Updated at"
\ No newline at end of file + updated_at: "Updated at" diff --git a/config/locales/layouts.fr.yml b/config/locales/layouts.fr.yml index 17d23c756..019c72701 100644 --- a/config/locales/layouts.fr.yml +++ b/config/locales/layouts.fr.yml @@ -10,9 +10,28 @@ fr: navbar: return_to_referentials: "Retour à la liste des espaces de données" select_referential: "Sélection de l'espace de données" + select_referential_for_more_features: "Sélectionnez un jeu de données pour accéder à plus de fonctionnalités" select_referential_datas: "Sélection des données" return_to_dashboard: "Retour au Tableau de Bord" + dashboard: Tableau de bord referential_datas: "Données" + line_referential: "Référentiel de lignes" + stop_area_referential: "Référentiel d'arrêts" + current_offer: + one: Offres courante + other: Offres courantes + workbench_outputs: + organisation: Offre de mon organisation + workgroup: Offre du groupe de travail + tools: Outils + sync: Synchronisation + sync_icar: Synchronisation iCAR + sync_ilico: Synchronisation iLICO + icar: iCAR + ilico: iLICO + portal: Portail (POSTIF) + support: Support + shapes: Tracés history_tag: title: "Métadonnées" created_at: "Créé le" diff --git a/config/locales/line_referential_syncs.en.yml b/config/locales/line_referential_syncs.en.yml index 5cbab1173..8d17fcefc 100644 --- a/config/locales/line_referential_syncs.en.yml +++ b/config/locales/line_referential_syncs.en.yml @@ -1,4 +1,6 @@ en: + line_referential_syncs: + search_no_results: "No line referential synchronisation matching your query" activerecord: errors: models: diff --git a/config/locales/line_referential_syncs.fr.yml b/config/locales/line_referential_syncs.fr.yml index 33827fac9..7f1b1ba75 100644 --- a/config/locales/line_referential_syncs.fr.yml +++ b/config/locales/line_referential_syncs.fr.yml @@ -1,4 +1,6 @@ fr: + line_referential_syncs: + search_no_results: "Aucun synchronisation de référentiel de lignes ne correspond à votre recherche" activerecord: errors: models: diff --git a/config/locales/line_referentials.en.yml b/config/locales/line_referentials.en.yml index 18ff28c24..6c15b340a 100644 --- a/config/locales/line_referentials.en.yml +++ b/config/locales/line_referentials.en.yml @@ -9,7 +9,7 @@ en: show: title: Line referential synchronized: Synchronized - status: Status + state: Status message: Message activerecord: models: diff --git a/config/locales/lines.en.yml b/config/locales/lines.en.yml index 1cd5150db..c1f9063a7 100644 --- a/config/locales/lines.en.yml +++ b/config/locales/lines.en.yml @@ -19,6 +19,7 @@ en: show: 'Show' show_network: 'Show network' show_company: 'Show company' + search_no_results: "No results found" filters: name_or_objectid_cont: "Search by name or objectid" new: @@ -78,7 +79,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..487cae35c 100644 --- a/config/locales/lines.fr.yml +++ b/config/locales/lines.fr.yml @@ -19,6 +19,7 @@ fr: show: 'Consulter' show_network: 'Voir le réseau' show_company: 'Voir le transporteur principal' + search_no_results: "Aucun résultat" filters: name_or_objectid_cont: "Indiquez un nom d'itinéraire ou un ID..." new: @@ -79,7 +80,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/merges.en.yml b/config/locales/merges.en.yml new file mode 100644 index 000000000..f9e90f1ef --- /dev/null +++ b/config/locales/merges.en.yml @@ -0,0 +1,23 @@ +fr: + merges: + referential_name: "Merged offer %{date}" + index: + title: "Merged offers" + new: + title: "New merged offer" + show: + title: "Merged offer %{name}" + actions: + create: Merge data spaces + activerecord: + models: + merge: "Merged offer" + attributes: + merge: + created_at: "Created at" + started_at: Started at + name: Name + ended_at: Ended at + status: "Status" + creator: "Creator" + referentials: "Data spaces" diff --git a/config/locales/merges.yml b/config/locales/merges.fr.yml index d8511a7b4..345727b74 100644 --- a/config/locales/merges.yml +++ b/config/locales/merges.fr.yml @@ -8,7 +8,7 @@ fr: show: title: "Finalisation de l'offre %{name}" actions: - create: "Finaliser des Jeux de Données" + create: Finaliser des Jeux de Données activerecord: models: merge: "Finalisation de l'offre" 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/config/locales/stop_area_referential_syncs.en.yml b/config/locales/stop_area_referential_syncs.en.yml index 2bfe0bc0a..1605f73f2 100644 --- a/config/locales/stop_area_referential_syncs.en.yml +++ b/config/locales/stop_area_referential_syncs.en.yml @@ -1,4 +1,6 @@ en: + stop_area_referential_syncs: + search_no_results: "No stop area referential synchronisation matching your query" activerecord: errors: models: diff --git a/config/locales/stop_area_referential_syncs.fr.yml b/config/locales/stop_area_referential_syncs.fr.yml index cbed11883..b4ed7202b 100644 --- a/config/locales/stop_area_referential_syncs.fr.yml +++ b/config/locales/stop_area_referential_syncs.fr.yml @@ -1,4 +1,6 @@ fr: + stop_area_referential_syncs: + search_no_results: "Aucun synchronisation de référentiel d'arrêts ne correspond à votre recherche" activerecord: errors: models: diff --git a/config/locales/stop_area_referentials.en.yml b/config/locales/stop_area_referentials.en.yml index 9d49d7c5d..3fa91bc92 100644 --- a/config/locales/stop_area_referentials.en.yml +++ b/config/locales/stop_area_referentials.en.yml @@ -4,7 +4,10 @@ en: sync: "Launch a new reflex synchronization" cancel_sync: "Cancel reflex synchronization" show: - title: 'Stop area referential' + title: 'Synchronization iCAR' + synchronized: Synchronized + status: status + message: Message activerecord: models: stop_area_referential: diff --git a/config/locales/stop_area_referentials.fr.yml b/config/locales/stop_area_referentials.fr.yml index bb4c4463a..956e801f1 100644 --- a/config/locales/stop_area_referentials.fr.yml +++ b/config/locales/stop_area_referentials.fr.yml @@ -5,6 +5,9 @@ fr: cancel_sync: "Annuler la synchronisation Reflex" show: title: 'Synchronisation iCAR' + synchronized: Synchronisé + status: Statut + message: Message activerecord: models: stop_area_referential: diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml index 12d8d0da4..8bc268197 100644 --- a/config/locales/vehicle_journeys.en.yml +++ b/config/locales/vehicle_journeys.en.yml @@ -4,7 +4,7 @@ en: filters: id: Filter by ID... journey_pattern: Filter by journey pattern... - timetable: Filter by timetable... + 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" @@ -133,6 +133,7 @@ en: journey_pattern_id: "Pattern ID" journey_pattern: "Journey Pattern" journey_pattern_published_name: "Journey Pattern published name" + journey_length: "Journey length" line: "Line" mobility_restricted_suitability: "PRM accessibility" name: "Journey Name" diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml index 466eca684..18703be9b 100644 --- a/config/locales/vehicle_journeys.fr.yml +++ b/config/locales/vehicle_journeys.fr.yml @@ -134,6 +134,7 @@ fr: journey_pattern_id: "ID Mission" journey_pattern: "Mission" journey_pattern_published_name: "Nom public de la mission" + journey_length: "Parcours commercial" line: "Ligne" mobility_restricted_suitability: "Accessibilité PMR" name: "Nom Course" diff --git a/config/locales/workbench_outputs.en.yml b/config/locales/workbench_outputs.en.yml index 05cf52d0e..2af2f7023 100644 --- a/config/locales/workbench_outputs.en.yml +++ b/config/locales/workbench_outputs.en.yml @@ -1,7 +1,7 @@ en: workbench_outputs: show: - title: "Finalisations de l'offre" - see_current_output: "Voir l'Offre actuelle" + title: Current offers + see_current_output: See the current offer table_headers: - ended_at: "Offer created date" + ended_at: "Ended at" diff --git a/config/locales/workbench_outputs.fr.yml b/config/locales/workbench_outputs.fr.yml index 560888c54..b4d339434 100644 --- a/config/locales/workbench_outputs.fr.yml +++ b/config/locales/workbench_outputs.fr.yml @@ -1,7 +1,7 @@ fr: workbench_outputs: show: - title: "Finalisations de l'offre" + title: Finaliser des jeux de données see_current_output: "Voir l'Offre actuelle" table_headers: - ended_at: "Date et heure de création de l'offre" + ended_at: "Date et heure de création" diff --git a/config/locales/zzz-stif.yml b/config/locales/zzz-stif.yml new file mode 100644 index 000000000..a9e6a0ef4 --- /dev/null +++ b/config/locales/zzz-stif.yml @@ -0,0 +1,5 @@ +fr: + layouts: + navbar: + workbench_outputs: + workgroup: Offre IDF diff --git a/config/webpacker.yml b/config/webpacker.yml index be1105bad..24a7e32dc 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -41,7 +41,7 @@ development: public: localhost:3035 hmr: false # Inline should be set to true if using HMR - inline: true + inline: false overlay: true disable_host_check: true use_local_ip: false diff --git a/db/migrate/20180413082929_clean_lines_secondary_companies.rb b/db/migrate/20180413082929_clean_lines_secondary_companies.rb new file mode 100644 index 000000000..4c5659e8f --- /dev/null +++ b/db/migrate/20180413082929_clean_lines_secondary_companies.rb @@ -0,0 +1,5 @@ +class CleanLinesSecondaryCompanies < ActiveRecord::Migration + def up + Chouette::Line.where("secondary_company_ids = '{NULL}'").update_all secondary_company_ids: nil + end +end 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/lib/tasks/referential.rake b/lib/tasks/referential.rake index d53157312..b3202035b 100644 --- a/lib/tasks/referential.rake +++ b/lib/tasks/referential.rake @@ -70,7 +70,7 @@ namespace :referential do def update_checksums_for_referential referential thing = %w(\\ | / —) - Referential.force_register_models_with_checksum if Rails.env.development? && Referential.models_with_checksum.empty? + Referential.force_register_models_with_checksum puts "\n \e[33m***\e[0m Referential #{referential.name}" referential.switch do Referential.models_with_checksum.each do |klass| diff --git a/package.json b/package.json index 262d80b97..ef956105c 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "clean-webpack-plugin": "^0.1.18", "coffee-loader": "^0.9.0", "coffeescript": "1.12.7", + "es6-symbol": "^3.1.1", "jquery": "3.2.1", "lodash": "4.17.4", + "polyfill-array-includes": "^1.0.0", "promise-polyfill": "7.0.0", "prop-types": "^15.6.0", "react": "16.2.0", @@ -24,7 +26,7 @@ "redux-promise": "0.5.3", "redux-thunk": "2.2.0", "uglify-js": "3.3.2", - "whatwg-fetch": "2.0.3" + "whatwg-fetch": "^2.0.4" }, "license": "MIT", "engines": { 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/controllers/lines_controller_spec.rb b/spec/controllers/lines_controller_spec.rb index 96f49bb36..78020484b 100644 --- a/spec/controllers/lines_controller_spec.rb +++ b/spec/controllers/lines_controller_spec.rb @@ -1,9 +1,36 @@ RSpec.describe LinesController, :type => :controller do login_user - let(:line_referential) { create :line_referential, member: @user.organisation } + let(:line_referential) { create :line_referential, member: @user.organisation, objectid_format: :netex } let(:line) { create :line, line_referential: line_referential } + describe 'POST create' do + let(:line_attrs){{ + name: "test", + transport_mode: "bus" + }} + let(:request){ post :create, line_referential_id: line_referential.id, line: line_attrs } + + with_permission "lines.create" do + it "should create a new line" do + expect{request}.to change{ line_referential.lines.count }.by 1 + end + + context "with an empty value in secondary_company_ids" do + let(:line_attrs){{ + name: "test", + transport_mode: "bus", + secondary_company_ids: [""] + }} + + it "should cleanup secondary_company_ids" do + expect{request}.to change{ line_referential.lines.count }.by 1 + expect(line_referential.lines.last.secondary_company_ids).to eq [] + end + end + end + end + describe 'PUT deactivate' do let(:request){ put :deactivate, id: line.id, line_referential_id: line_referential.id } 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/fixtures/test_1/file.txt b/spec/fixtures/test_1/file.txt new file mode 100644 index 000000000..3cd77f071 --- /dev/null +++ b/spec/fixtures/test_1/file.txt @@ -0,0 +1 @@ +TEST 1 diff --git a/spec/fixtures/test_2/file.txt b/spec/fixtures/test_2/file.txt new file mode 100644 index 000000000..55d8fa4b0 --- /dev/null +++ b/spec/fixtures/test_2/file.txt @@ -0,0 +1 @@ +TEST 2 diff --git a/spec/models/chouette/purchase_window_spec.rb b/spec/models/chouette/purchase_window_spec.rb index 702a44eeb..5e81108e1 100644 --- a/spec/models/chouette/purchase_window_spec.rb +++ b/spec/models/chouette/purchase_window_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Chouette::PurchaseWindow, :type => :model do describe 'validations' do it 'validates and date_ranges do not overlap' do expect(build(:purchase_window, referential: referential,date_ranges: [Date.today..Date.today + 10.day, Date.yesterday..Date.tomorrow])).to_not be_valid - # expect(build(periods: [Date.today..Date.today + 10.day, Date.yesterday..Date.tomorrow ])).to_not be_valid + expect(build(:purchase_window, referential: referential,date_ranges: [Date.today..Date.today])).to be_valid 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/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index 6d44eeb2f..8682d59a2 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 { should have_and_belong_to_many(:purchase_windows) } it "must be valid with an at-stop day offset of 1" do @@ -34,6 +34,46 @@ describe Chouette::VehicleJourney, :type => :model do expect(vehicle_journey).to receive(:update_checksum_without_callbacks!).at_least(:once).and_call_original expect{create(:vehicle_journey_at_stop, vehicle_journey: vehicle_journey)}.to change{vehicle_journey.checksum} end + + context "when custom_field_values change" do + let(:vehicle_journey){ create(:vehicle_journey, custom_field_values: {custom_field.code.to_s => former_value}) } + let(:custom_field){ create :custom_field, field_type: :string, code: :energy, name: :energy, resource_type: "VehicleJourney" } + let(:former_value){ "foo" } + let(:value){ "bar" } + before do + @checksum_source = vehicle_journey.checksum_source + end + + it "should update the checksum" do + vehicle_journey.custom_field_values = {custom_field.code.to_s => value} + vehicle_journey.save + expect(vehicle_journey.checksum_source).to_not eq @checksum_source + end + + context "with an attachment custom_field" do + let(:vehicle_journey) do + custom_field + vj = create(:vehicle_journey) + vj.custom_field_energy = File.open(fixtures_path("test_1/file.txt")) + vj.save + vj + end + let(:custom_field){ create :custom_field, field_type: :attachment, code: :energy, name: :energy, resource_type: "VehicleJourney" } + + it "should update the checksum" do + expect(CustomField::Instance::Attachment).to receive(:digest).and_call_original + vehicle_journey.custom_field_energy = File.open(fixtures_path("test_2/file.txt")) + vehicle_journey.save + expect(vehicle_journey.checksum_source).to_not eq @checksum_source + end + + it "should not calculate the digest if the vale does not change" do + expect(CustomField::Instance::Attachment).to_not receive(:digest).and_call_original + vehicle_journey.reload.save + expect(vehicle_journey.checksum_source).to eq @checksum_source + end + end + end end describe "#with_stop_area_ids" do 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 diff --git a/spec/views/line_referentials/show.html.slim_spec.rb b/spec/views/line_referentials/show.html.slim_spec.rb index 0516677cb..6734957c8 100644 --- a/spec/views/line_referentials/show.html.slim_spec.rb +++ b/spec/views/line_referentials/show.html.slim_spec.rb @@ -10,13 +10,11 @@ describe "/line_referentials/show", :type => :view do it "should not present syncing infos and button" do expect(view.content_for(:page_header_actions)).to_not have_selector("a[href=\"#{view.sync_line_referential_path(line_referential)}\"]") - expect(view.content_for(:page_header_meta)).to_not have_selector(".last-update") end with_permission "line_referentials.synchronize" do it "should present syncing infos and button" do expect(view.content_for(:page_header_actions)).to have_selector("a[href=\"#{view.sync_line_referential_path(line_referential)}\"]", count: 1) - expect(view.content_for(:page_header_meta)).to have_selector(".last-update", count: 1) end end end diff --git a/spec/views/line_referentials/stop_area_referentials/show.html.slim_spec.rb b/spec/views/stop_area_referentials/show.html.slim_spec.rb index 71a8d16f5..a7567a969 100644 --- a/spec/views/line_referentials/stop_area_referentials/show.html.slim_spec.rb +++ b/spec/views/stop_area_referentials/show.html.slim_spec.rb @@ -10,13 +10,11 @@ describe "/stop_area_referentials/show", :type => :view do it "should not present syncing infos and button" do expect(view.content_for(:page_header_actions)).to_not have_selector("a[href=\"#{view.sync_stop_area_referential_path(stop_area_referential)}\"]") - expect(view.content_for(:page_header_meta)).to_not have_selector(".last-update") end with_permission "stop_area_referentials.synchronize" do it "should present syncing infos and button" do expect(view.content_for(:page_header_actions)).to have_selector("a[href=\"#{view.sync_stop_area_referential_path(stop_area_referential)}\"]", count: 1) - expect(view.content_for(:page_header_meta)).to have_selector(".last-update", count: 1) end end end @@ -4719,6 +4719,10 @@ pn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" +polyfill-array-includes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/polyfill-array-includes/-/polyfill-array-includes-1.0.0.tgz#3dda070475859e99d653acf06ec3622cc76f8430" + portfinder@^1.0.9: version "1.0.13" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" @@ -6777,10 +6781,14 @@ whatwg-encoding@^1.0.1: dependencies: iconv-lite "0.4.13" -whatwg-fetch@2.0.3, whatwg-fetch@>=0.10.0: +whatwg-fetch@>=0.10.0: version "2.0.3" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" +whatwg-fetch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + whatwg-url@^6.3.0: version "6.4.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08" |
