diff options
33 files changed, 437 insertions, 101 deletions
| diff --git a/.gitignore b/.gitignore index dd4d057ef..28960565b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@  # Ignore all logfiles and tempfiles.  /log/*.log +/log/importers  /tmp  *~  public/assets/ @@ -66,7 +66,6 @@ end  gem 'activerecord-postgis-adapter', "~> 3.0.0"  gem 'polylines' -gem 'activerecord-nulldb-adapter', require: false  # Codifligne API  gem 'codifligne', af83: 'stif-codifline-api' @@ -142,7 +141,7 @@ gem 'rake'  gem 'devise-async'  gem 'apartment', '~> 1.0.0'  gem 'aasm' -gem 'activerecord-nulldb-adapter' +gem 'activerecord-nulldb-adapter' if ENV['RAILS_DB_ADAPTER'] == 'nulldb'  gem 'puma', '~> 3.10.0'  gem 'newrelic_rpm' diff --git a/Gemfile.lock b/Gemfile.lock index 046167e69..63d78f9cd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,8 +76,6 @@ GEM        activemodel (= 4.2.8)        activesupport (= 4.2.8)        arel (~> 6.0) -    activerecord-nulldb-adapter (0.3.7) -      activerecord (>= 2.0.0)      activerecord-postgis-adapter (3.0.0)        activerecord (~> 4.2)        rgeo-activerecord (~> 4.0) @@ -597,7 +595,6 @@ DEPENDENCIES    SyslogLogger    aasm    active_attr -  activerecord-nulldb-adapter    activerecord-postgis-adapter (~> 3.0.0)    acts-as-taggable-on (~> 4.0.0)    acts_as_list (~> 0.6.0) diff --git a/app/assets/stylesheets/components/_main_nav.sass b/app/assets/stylesheets/components/_main_nav.sass index 2af070389..8e164fa01 100644 --- a/app/assets/stylesheets/components/_main_nav.sass +++ b/app/assets/stylesheets/components/_main_nav.sass @@ -375,3 +375,16 @@ $menuW: 300px              + .btn                margin-left: 10px + +  .languages +    .dropdown-menu +      top: 0 +      right: 0 +      left: auto +      white-space: nowrap +      line-height: 11px +      min-width: 0 +      li +        display: inline-block +        a +          padding: 2px 10px diff --git a/app/assets/stylesheets/components/_referential_overview.sass b/app/assets/stylesheets/components/_referential_overview.sass index 0beb8ab67..fc48411a3 100644 --- a/app/assets/stylesheets/components/_referential_overview.sass +++ b/app/assets/stylesheets/components/_referential_overview.sass @@ -22,7 +22,7 @@        box-shadow: 0 0 10px rgba(0,0,0,0.5)        z-index: 1    .time-travel -    padding-top: 4px +    padding-top: 3px      padding-bottom: 4px      a.btn:first-child        margin-right: 1px @@ -61,7 +61,7 @@          border-color: $grey          width: auto          flex: 1 1 -        padding: 4px 11px 5px +        padding: 6px 11px          .input-group-btn            right: 10px          &.togglable diff --git a/app/controllers/compliance_control_sets_controller.rb b/app/controllers/compliance_control_sets_controller.rb index 8f9251155..6461b38c8 100644 --- a/app/controllers/compliance_control_sets_controller.rb +++ b/app/controllers/compliance_control_sets_controller.rb @@ -36,11 +36,15 @@ class ComplianceControlSetsController < ChouetteController    private    def collection -    scope = self.ransack_period_range(scope: ComplianceControlSet.all, error_message: t('imports.filters.error_period_filter'), query: :where_updated_at_between) -    @q_for_form = scope.ransack(params[:q]) -    compliance_control_sets = @q_for_form.result -    compliance_control_sets = joins_with_associated_objects(compliance_control_sets).order(sort_column + ' ' + sort_direction) if sort_column && sort_direction -    @compliance_control_sets = compliance_control_sets.paginate(page: params[:page], per_page: 30) +    @compliance_control_sets ||= begin +      scope = end_of_association_chain.all +      scope = self.ransack_period_range(scope: scope, error_message: t('imports.filters.error_period_filter'), query: :where_updated_at_between) +      @q_for_form = scope.ransack(params[:q]) +      compliance_control_sets = @q_for_form.result +      compliance_control_sets = joins_with_associated_objects(compliance_control_sets).order(sort_column + ' ' + sort_direction) if sort_column && sort_direction +      compliance_control_sets = compliance_control_sets.paginate(page: params[:page], per_page: 30) +    end +    end    def decorate_compliance_control_sets(compliance_control_sets) @@ -82,9 +86,9 @@ class ComplianceControlSetsController < ChouetteController      case params[:sort]        when 'owner_jdc'          collection.joins("LEFT JOIN organisations ON compliance_control_sets.organisation_id = organisations.id") -      when 'control_numbers'  +      when 'control_numbers'          collection.joins("LEFT JOIN compliance_controls ON compliance_controls.compliance_control_set_id = compliance_control_sets.id").group(:id) -      else  +      else          collection      end    end diff --git a/app/controllers/concerns/feature_checker.rb b/app/controllers/concerns/feature_checker.rb index 9ca5ed0a7..5e102ef1b 100644 --- a/app/controllers/concerns/feature_checker.rb +++ b/app/controllers/concerns/feature_checker.rb @@ -27,6 +27,8 @@ module FeatureChecker    protected    def has_feature?(*features) +    return false unless current_organisation +      features.all? do |feature|        current_organisation.has_feature? feature      end diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index b398d78fa..e67753e4b 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -92,7 +92,9 @@ const actions = {        id: selectedTT.id,        comment: selectedTT.comment,        objectid: selectedTT.objectid, -      color: selectedTT.color +      color: selectedTT.color, +      bounding_dates: selectedTT.time_table_bounding, +      days: selectedTT.day_types      }    }),    addSelectedTimetable: () => ({ diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index e11e91497..7db0cee1c 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -66,6 +66,7 @@ export default class VehicleJourney extends Component {    render() {      this.previousCity = undefined      let detailed_calendars = this.hasFeature('detailed_calendars') && !this.disabled +    let detailed_calendars_shown = $('.detailed-timetables-bt').hasClass('active')      let {time_tables, purchase_windows} = this.props.value      return ( @@ -110,7 +111,7 @@ export default class VehicleJourney extends Component {            {this.props.disabled && <VehicleJourneyInfoButton vehicleJourney={this.props.value} />}            { detailed_calendars && -            <div className="detailed-timetables hidden"> +            <div className={"detailed-timetables" + (detailed_calendars_shown ? "" : " hidden")}>              {this.props.allTimeTables.map((tt, i) =>                <div key={i} className={(this.hasTimeTable(time_tables, tt) ? "active" : "inactive")}></div>              )} diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index 843aec1a8..384afba17 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -187,7 +187,7 @@ export default class VehicleJourneys extends Component {                            <p>                              {this.timeTableURL(tt)}                            </p> -                          <p>{tt.bounding_dates}</p> +                          <p>{tt.bounding_dates.split(' ').join(' > ')}</p>                          </div>                        )}                      </div> diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js index 8536f66e6..24d9a23c2 100644 --- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js +++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js @@ -117,6 +117,11 @@ export default class CreateModal extends Component {                                      className='form-control'                                      onKeyDown={(e) => actions.resetValidation(e.currentTarget)}                                      /> +                                  <input +                                    type='hidden' +                                    ref='tz_offset' +                                    value={new Date().getTimezoneOffset()} +                                    />                                  </div>                                </div>                              </div> diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index 383dea4a0..8705b3cf2 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -14,47 +14,73 @@ const vehicleJourney= (state = {}, action, keep) => {          hour: 0,          minute: 0        } -      if(action.data["start_time.hour"] && action.data["start_time.minute"] && action.selectedJourneyPattern.full_schedule){ -        current_time.hour = parseInt(action.data["start_time.hour"].value) -        current_time.minute = parseInt(action.data["start_time.minute"].value) || 0 +      let computeSchedule = false +      let userTZOffet = 0 +      if(action.data["start_time.hour"] && action.data["start_time.hour"].value && action.data["start_time.hour"].value.length > 0 && action.data["start_time.minute"] && action.selectedJourneyPattern.full_schedule && action.selectedJourneyPattern.costs){ +        computeSchedule = true +        userTZOffet = action.data["tz_offset"] && parseInt(action.data["tz_offset"].value) || 0 +        current_time.hour = parseInt(action.data["start_time.hour"].value) + parseInt(userTZOffet / 60) +        current_time.minute = 0 +        if(action.data["start_time.minute"].value){ +          current_time.minute = parseInt(action.data["start_time.minute"].value) + (userTZOffet - 60 * parseInt(userTZOffet / 60)) +        }        }        _.each(action.stopPointsList, (sp) =>{          let inJourney = false -        if(action.selectedJourneyPattern.full_schedule && action.selectedJourneyPattern.costs && action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id]){ -          let delta = parseInt(action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id].time) -          current_time = actions.addMinutesToTime(current_time, delta) -          prevSp = sp -          inJourney = true -        } -        let offsetHours = sp.time_zone_offset / 3600 -        let offsetminutes = sp.time_zone_offset/60 - 60*offsetHours -        let newVjas = { -          delta: 0, -          arrival_time:{ -            hour: (24 + current_time.hour + offsetHours) % 24, -            minute: current_time.minute + offsetminutes -          }, -          stop_point_objectid: sp.object_id, -          stop_area_cityname: sp.city_name, -          dummy: true -        } +        let newVjas +        if(computeSchedule){ +          if(action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id]){ +            let delta = parseInt(action.selectedJourneyPattern.costs[prevSp.stop_area_id + "-" + sp.stop_area_id].time) +            current_time = actions.addMinutesToTime(current_time, delta) +            prevSp = sp +            inJourney = true +          } +          let offsetHours = sp.time_zone_offset / 3600 +          let offsetminutes = sp.time_zone_offset/60 - 60*offsetHours +          newVjas = { +            delta: 0, +            arrival_time:{ +              hour: (24 + current_time.hour + offsetHours) % 24, +              minute: current_time.minute + offsetminutes +            }, +            stop_point_objectid: sp.object_id, +            stop_area_cityname: sp.city_name, +            dummy: true +          } -        if(sp.waiting_time && inJourney){ -          current_time = actions.addMinutesToTime(current_time, parseInt(sp.waiting_time)) -        } +          if(sp.waiting_time && inJourney){ +            current_time = actions.addMinutesToTime(current_time, parseInt(sp.waiting_time)) +          } -        newVjas.departure_time = { -          hour: (24 + current_time.hour + offsetHours) % 24, -          minute: current_time.minute + offsetminutes -        } +          newVjas.departure_time = { +            hour: (24 + current_time.hour + offsetHours) % 24, +            minute: current_time.minute + offsetminutes +          } -        if(current_time.hour + offsetHours > 24){ -          newVjas.departure_day_offset = 1 -          newVjas.arrival_day_offset = 1 +          if(current_time.hour + offsetHours > 24){ +            newVjas.departure_day_offset = 1 +            newVjas.arrival_day_offset = 1 +          } +          if(current_time.hour + offsetHours < 0){ +            newVjas.departure_day_offset = -1 +            newVjas.arrival_day_offset = -1 +          }          } -        if(current_time.hour + offsetHours < 0){ -          newVjas.departure_day_offset = -1 -          newVjas.arrival_day_offset = -1 +        else{ +          newVjas = { +            delta: 0, +            arrival_time: { +              hour: 0, +              minute: 0 +            }, +            departure_time: { +              hour: 0, +              minute: 0 +            }, +            stop_point_objectid: sp.object_id, +            stop_area_cityname: sp.city_name, +            dummy: true +          }          }          _.each(action.selectedJourneyPattern.stop_areas, (jp) =>{ diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 6209993de..9b94f7f0e 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -93,7 +93,7 @@ module Chouette      scope :in_purchase_window, ->(range){        purchase_windows = Chouette::PurchaseWindow.overlap_dates(range)        sql = purchase_windows.joins(:vehicle_journeys).select('vehicle_journeys.id').uniq.to_sql -      where("id IN (#{sql})") +      where("vehicle_journeys.id IN (#{sql})")      }      # We need this for the ransack object in the filters diff --git a/app/models/merge.rb b/app/models/merge.rb index 62bf581d6..d42d882ac 100644 --- a/app/models/merge.rb +++ b/app/models/merge.rb @@ -152,7 +152,7 @@ class Merge < ActiveRecord::Base            route_stop_points = referential_stop_points_by_route[route.id]            # Stop Points -          route_stop_points.each do |stop_point| +          route_stop_points.sort_by(&:position).each do |stop_point|              objectid = Chouette::StopPoint.where(objectid: stop_point.objectid).exists? ? nil : stop_point.objectid              attributes = stop_point.attributes.merge(                id: nil, @@ -166,7 +166,7 @@ class Merge < ActiveRecord::Base            new_route.save!            if new_route.checksum != route.checksum -            raise "Checksum has changed: #{route.inspect} #{new_route.inspect}" +            raise "Checksum has changed: \"#{route.checksum}\", \"#{route.checksum_source}\" -> \"#{new_route.checksum}\", \"#{new_route.checksum_source}\""            end          end        end @@ -221,7 +221,7 @@ class Merge < ActiveRecord::Base            new_journey_pattern = new.journey_patterns.create! attributes            if new_journey_pattern.checksum != journey_pattern.checksum -            raise "Checksum has changed for #{journey_pattern.inspect}: #{journey_pattern.checksum_source} #{new_journey_pattern.checksum_source} " +            raise "Checksum has changed for #{journey_pattern.inspect}: \"#{journey_pattern.checksum_source}\" -> \"#{new_journey_pattern.checksum_source}\""            end          end        end diff --git a/app/models/referential.rb b/app/models/referential.rb index 09c2e7d34..91a88d02d 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -408,6 +408,7 @@ class Referential < ActiveRecord::Base        end        check_migration_count(report) +      # raise "Wrong migration count: #{migration_count}" if migration_count < 300      end    end @@ -417,13 +418,20 @@ class Referential < ActiveRecord::Base    end    def migration_count -    if self.class.connection.table_exists?("#{slug}.schema_migrations") -      self.class.connection.select_value("select count(*) from #{slug}.schema_migrations;") -    end +    raw_value = +      if self.class.connection.table_exists?("#{slug}.schema_migrations") +        self.class.connection.select_value("select count(*) from #{slug}.schema_migrations;") +      end + +    raw_value.to_i    end -  def assign_slug -    self.slug ||= "#{name.parameterize.gsub('-', '_')}_#{Time.now.to_i}" if name +  def assign_slug(time_reference = Time) +    self.slug ||= begin +      prefix = name.parameterize.gsub('-','_').gsub(/[^a-zA-Z_]/,'').gsub(/^_/,'') +      prefix = "referential" if prefix.blank? +      "#{prefix}_#{time_reference.now.to_i}" +    end if name    end    def assign_prefix diff --git a/app/models/simple_importer.rb b/app/models/simple_importer.rb index b824d596d..d6ba64494 100644 --- a/app/models/simple_importer.rb +++ b/app/models/simple_importer.rb @@ -95,7 +95,8 @@ class SimpleImporter < ActiveRecord::Base    end    def dump_csv_from_context -    filepath = "./#{self.configuration_name}_#{Time.now.strftime "%y%m%d%H%M"}.csv" +    dir = context[:output_dir] || "log/importers" +    filepath = File.join dir, "#{self.configuration_name}_#{Time.now.strftime "%y%m%d%H%M"}.csv"      # for some reason, context[:csv].to_csv does not work      CSV.open(filepath, 'w') do |csv|        header = true @@ -262,7 +263,7 @@ class SimpleImporter < ActiveRecord::Base        msg += "\n\n"        msg += colorize "=== MESSAGES (#{@messages.count}) ===\n", :green        msg += "[...]\n" if @messages.count > lines_count -      msg += @messages.last(lines_count).join("\n") +      msg += @messages.last(lines_count).map{|m| m.truncate(@status_width)}.join("\n")        msg += "\n"*[lines_count-@messages.count, 0].max      end @@ -273,7 +274,9 @@ class SimpleImporter < ActiveRecord::Base        msg += @errors.last(lines_count).map do |j|          kind = j[:kind]          kind = colorize(kind, kind == :error ? :red : :orange) -        encode_string "[#{kind}]\t\tL#{j[:line]}\t#{j[:error]}\t\t#{j[:message]}" +        kind = "[#{kind}]" +        kind += " "*(25 - kind.size) +        encode_string("#{kind}L#{j[:line]}\t#{j[:error]}\t\t#{j[:message]}").truncate(@status_width)        end.join("\n")      end      custom_print msg, clear: true diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index c44937c9e..33d88660c 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -95,7 +95,6 @@ class ApplicationPolicy      referential.try(:organisation_id) || record.try(:organisation_id)    end -    #    #  Helpers    #  ------- diff --git a/app/policies/compliance_control_set_policy.rb b/app/policies/compliance_control_set_policy.rb index 011f6c0c7..55507ffd9 100644 --- a/app/policies/compliance_control_set_policy.rb +++ b/app/policies/compliance_control_set_policy.rb @@ -5,6 +5,10 @@ class ComplianceControlSetPolicy < ApplicationPolicy      end    end +  def show? +    organisation_match? +  end +    def destroy?      user.has_permission?('compliance_control_sets.destroy')    end diff --git a/app/views/compliance_controls/_filters.html.slim b/app/views/compliance_controls/_filters.html.slim index f6b9970f2..a16d2c33d 100644 --- a/app/views/compliance_controls/_filters.html.slim +++ b/app/views/compliance_controls/_filters.html.slim @@ -5,8 +5,8 @@    class: 'form form-filter' do |f|    .ffg-row -    .input-group.search_bar class=filter_item_class(params[:q], :name_cont) -      = f.search_field :name_cont, +    .input-group.search_bar class=filter_item_class(params[:q], :name_or_code_cont) +      = f.search_field :name_or_code_cont,          class: 'form-control',          placeholder: t('compliance_controls.filters.name')        span.input-group-btn diff --git a/app/views/layouts/navigation/_main_nav_top.html.slim b/app/views/layouts/navigation/_main_nav_top.html.slim index f664d5416..12355dfb7 100644 --- a/app/views/layouts/navigation/_main_nav_top.html.slim +++ b/app/views/layouts/navigation/_main_nav_top.html.slim @@ -21,6 +21,18 @@          = link_to destroy_user_session_path, method: :delete, class: 'menu-item', title: 'Se déconnecter' do            span.fa.fa-lg.fa-sign-out +    - if has_feature?(:change_locale) +      .menu-item-group.pull-right +        .dropdown.languages +          a href="#" class="dropdown-toggle" data-toggle="dropdown" +            = image_tag("language_engine/#{selected_language}_flag.png", { :'data-locale' => "#{selected_language}" } ) +            b.caret + +          ul.dropdown-menu +            - I18n.available_locales.each do |locale| +              li= link_to_language locale, { :class => language_class( locale ) } + +    = render 'layouts/navigation/nav_panel_operations'    = render 'layouts/navigation/nav_panel_profile' if user_signed_in? diff --git a/app/views/referential_vehicle_journeys/index.html.slim b/app/views/referential_vehicle_journeys/index.html.slim index 04c01cc12..00f63cb65 100644 --- a/app/views/referential_vehicle_journeys/index.html.slim +++ b/app/views/referential_vehicle_journeys/index.html.slim @@ -39,15 +39,20 @@                  ), \                  TableBuilderHelper::Column.new( \                    key: :departure_time, \ -                  attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure }, \ -                  sortable: false, \ -                  name: @starting_stop&.name, \ +                  attribute: Proc.new {|v| v.vehicle_journey_at_stops.first&.departure_local }, \ +                  sortable: true \                  ), \ +                [@starting_stop, @ending_stop].compact.map{|stop| \ +                  TableBuilderHelper::Column.new( \ +                    attribute: Proc.new {|v| v.vehicle_journey_at_stops.where("stop_points.stop_area_id" => stop.id).last&.arrival_local }, \ +                    sortable: false, \ +                    name: stop.name \ +                  )\ +                }, \                  TableBuilderHelper::Column.new( \                    key: :arrival_time, \ -                  attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival }, \ -                  sortable: false, \ -                  name: @ending_stop&.name, \ +                  attribute: Proc.new {|v| v.vehicle_journey_at_stops.last&.arrival_local }, \ +                  sortable: true, \                  ), \                ].flatten.compact,                cls: 'table has-filter has-search' diff --git a/app/views/referentials/_overview.html.slim b/app/views/referentials/_overview.html.slim index 539c25fd4..870f642d4 100644 --- a/app/views/referentials/_overview.html.slim +++ b/app/views/referentials/_overview.html.slim @@ -13,7 +13,7 @@          .form-group.togglable            = f.label Chouette::Line.human_attribute_name(:transport_mode), required: false, class: 'control-label' -          = f.input :transport_mode_eq_any, collection: overview.referential_lines.map(&:transport_mode).uniq.sort, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + t("enumerize.transport_mode.#{l}") + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'} +          = f.input :transport_mode_eq_any, collection: overview.referential_lines.map(&:transport_mode).compact.uniq.sort, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + t("enumerize.transport_mode.#{l}") + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}        .actions          = link_to 'Effacer', url_for() + "##{overview.pagination_param_name}", class: 'btn btn-link' @@ -39,7 +39,7 @@              a.number style="background-color: #{line.color.present? ? "##{line.color}" : 'whitesmoke'}" title=line.name                = line.number              .company= line.company&.name -            .mode= t("enumerize.transport_mode.#{line.transport_mode}") +            .mode= line.transport_mode.present? ? t("enumerize.transport_mode.#{line.transport_mode}") : ""      .right        .inner          .head diff --git a/app/views/time_tables/_form.html.slim b/app/views/time_tables/_form.html.slim index 007044e65..000da870e 100644 --- a/app/views/time_tables/_form.html.slim +++ b/app/views/time_tables/_form.html.slim @@ -5,7 +5,7 @@        = form.input :comment, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.comment")}        - if @time_table.new_record? && !@time_table.created_from -        = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_workgroup_calendars_path(current_workgroup)}} +        = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_workgroup_calendars_path(@referential.workgroup)}}        - if @time_table.created_from          = form.input :created_from, disabled: true, input_html: { value: @time_table.created_from.comment } diff --git a/app/workers/workbench_import_worker.rb b/app/workers/workbench_import_worker.rb index 6420be835..53cbb222a 100644 --- a/app/workers/workbench_import_worker.rb +++ b/app/workers/workbench_import_worker.rb @@ -53,17 +53,20 @@ class WorkbenchImportWorker    end    def upload_entry_group_stream eg_name, eg_stream -    FileUtils.mkdir_p(Rails.root.join('tmp', 'imports')) +    FileUtils.mkdir_p(temp_directory) -    File.open(Rails.root.join('tmp', 'imports', "WorkbenchImport_#{eg_name}_#{$$}.zip"), 'wb') do |file| +    eg_file_path = Tempfile.open( +      ["WorkbenchImport_#{eg_name}_", '.zip'], +      temp_directory +    ) do |f|        eg_stream.rewind -      file.write eg_stream.read +      f.write eg_stream.read + +      f.path      end -    upload_entry_group_tmpfile eg_name, File.new(Rails.root.join('tmp', 'imports', "WorkbenchImport_#{eg_name}_#{$$}.zip")) -  end -     -  def upload_entry_group_tmpfile eg_name, eg_file +    eg_file = File.open(eg_file_path) +      result = execute_post eg_name, eg_file      if result && result.status < 400        @entries += 1 @@ -73,8 +76,8 @@ class WorkbenchImportWorker        raise StopIteration, result.body      end    ensure -    eg_file.close rescue nil -    eg_file.unlink rescue nil +    eg_file.close +    File.unlink(eg_file.path)    end @@ -117,6 +120,11 @@ class WorkbenchImportWorker          file: HTTPService.upload(file, 'application/zip', "#{name}.zip") } }    end +  def temp_directory +    Rails.application.config.try(:import_temporary_directory) || +      Rails.root.join('tmp', 'imports') +  end +    # Lazy Values    # =========== diff --git a/config/breadcrumbs.rb b/config/breadcrumbs.rb index 00ccf16bd..a7cdb4313 100644 --- a/config/breadcrumbs.rb +++ b/config/breadcrumbs.rb @@ -8,7 +8,7 @@ end  crumb :workbench_output do |workbench|    link I18n.t('workbench_outputs.show.title'), workbench_output_path(workbench) -  parent :workbench, current_offer_workbench +  parent :workbench, mutual_workbench(workbench)  end  crumb :merges do |workbench| diff --git a/config/initializers/apartment.rb b/config/initializers/apartment.rb index fc652a2da..a996549fd 100644 --- a/config/initializers/apartment.rb +++ b/config/initializers/apartment.rb @@ -98,7 +98,7 @@ Apartment.configure do |config|    # config.append_environment = true    # supply list of database names for migrations to run on -  config.tenant_names = lambda{  Referential.order("created_from_id asc").pluck(:slug) } +  config.tenant_names = lambda{  Referential.where(ready: true).order("created_from_id asc").pluck(:slug) }  end  ## diff --git a/config/locales/area_types.fr.yml b/config/locales/area_types.fr.yml index bb249c235..71c26df92 100644 --- a/config/locales/area_types.fr.yml +++ b/config/locales/area_types.fr.yml @@ -10,5 +10,5 @@ fr:        deposit: Dépôt        border: Frontière        service_area: Aire de service / Pause -      relief: Point de releve +      relief: Point de relève        other: Autre diff --git a/db/schema.rb b/db/schema.rb index f77961f8d..c709290f5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -18,7 +18,6 @@ ActiveRecord::Schema.define(version: 20180202170009) do    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 diff --git a/lib/tasks/imports.rake b/lib/tasks/imports.rake index b91ff7efb..f01d3f34f 100644 --- a/lib/tasks/imports.rake +++ b/lib/tasks/imports.rake @@ -11,8 +11,8 @@ namespace :import do      NetexImport.abort_old    end -  def importer_output_to_csv importer -    filepath = "./#{importer.configuration_name}_#{Time.now.strftime "%y%m%d%H%M"}_out.csv" +  def importer_output_to_csv importer, output_dir +    filepath =  File.join output_dir, + "#{importer.configuration_name}_#{Time.now.strftime "%y%m%d%H%M"}_out.csv"      cols = %w(line kind event message error)      if importer.reload.journal.size > 0        keys = importer.journal.first["row"].map(&:first) @@ -27,13 +27,17 @@ namespace :import do    end    desc "import the given file with the corresponding importer" -  task :import, [:configuration_name, :filepath, :referential_id] => :environment do |t, args| +  task :import, [:configuration_name, :filepath, :referential_id, :output_dir] => :environment do |t, args| +    args.with_defaults(output_dir: "./log/importers/") +    FileUtils.mkdir_p args[:output_dir] +      importer = SimpleImporter.create configuration_name: args[:configuration_name], filepath: args[:filepath] +      if args[:referential_id].present?        referential = Referential.find args[:referential_id]        importer.configure do |config|          config.add_value :referential, referential -        config.context = {referential: referential} +        config.context = {referential: referential, output_dir: args[:output_dir]}        end      end      puts "\e[33m***\e[0m Start importing" @@ -43,17 +47,20 @@ namespace :import do        raise      ensure        puts "\n\e[33m***\e[0m Import done, status: " + (importer.status == "success" ? "\e[32m" : "\e[31m" ) + (importer.status || "") + "\e[0m" -      importer_output_to_csv importer +      importer_output_to_csv importer, args[:output_dir]      end    end    desc "import the given file with the corresponding importer in the given StopAreaReferential"    task :import_in_stop_area_referential, [:referential_id, :configuration_name, :filepath] => :environment do |t, args| +    args.with_defaults(output_dir: "./log/importers/") +    FileUtils.mkdir_p args[:output_dir] +      referential = StopAreaReferential.find args[:referential_id]      importer = SimpleImporter.create configuration_name: args[:configuration_name], filepath: args[:filepath]      importer.configure do |config|        config.add_value :stop_area_referential, referential -      config.context = {stop_area_referential: referential} +      config.context = {stop_area_referential: referential, output_dir: args[:output_dir]}      end      puts "\e[33m***\e[0m Start importing"      begin @@ -62,19 +69,22 @@ namespace :import do        raise      ensure        puts "\n\e[33m***\e[0m Import done, status: " + (importer.status == "success" ? "\e[32m" : "\e[31m" ) + (importer.status || "") + "\e[0m" -      importer_output_to_csv importer +      importer_output_to_csv importer, args[:output_dir]      end    end    desc "import the given routes files"    task :import_routes, [:referential_id, :configuration_name, :mapping_filepath, :filepath] => :environment do |t, args| +    args.with_defaults(output_dir: "./log/importers/") +    FileUtils.mkdir_p args[:output_dir] +      referential = Referential.find args[:referential_id]      referential.switch      stop_area_referential = referential.stop_area_referential      importer = SimpleImporter.create configuration_name: args[:configuration_name], filepath: args[:filepath]      importer.configure do |config|        config.add_value :stop_area_referential, referential -      config.context = {stop_area_referential: stop_area_referential, mapping_filepath: args[:mapping_filepath]} +      config.context = {stop_area_referential: stop_area_referential, mapping_filepath: args[:mapping_filepath], output_dir: args[:output_dir]}      end      puts "\e[33m***\e[0m Start importing"      begin @@ -83,17 +93,20 @@ namespace :import do        raise      ensure        puts "\n\e[33m***\e[0m Import done, status: " + (importer.status == "success" ? "\e[32m" : "\e[31m" ) + (importer.status || "") + "\e[0m" -      importer_output_to_csv importer +      importer_output_to_csv importer, args[:output_dir]      end    end    desc "import the given file with the corresponding importer in the given LineReferential" -  task :import_in_line_referential, [:referential_id, :configuration_name, :filepath] => :environment do |t, args| +  task :import_in_line_referential, [:referential_id, :configuration_name, :filepath, :output_dir] => :environment do |t, args| +    args.with_defaults(output_dir: "./log/importers/") +    FileUtils.mkdir_p args[:output_dir] +      referential = LineReferential.find args[:referential_id]      importer = SimpleImporter.create configuration_name: args[:configuration_name], filepath: args[:filepath]      importer.configure do |config|        config.add_value :line_referential, referential -      config.context = {line_referential: referential} +      config.context = {line_referential: referential, output_dir: args[:output_dir]}      end      puts "\e[33m***\e[0m Start importing"      begin @@ -102,7 +115,7 @@ namespace :import do        raise      ensure        puts "\n\e[33m***\e[0m Import done, status: " + (importer.status == "success" ? "\e[32m" : "\e[31m" ) + (importer.status || "") + "\e[0m" -      importer_output_to_csv importer +      importer_output_to_csv importer, args[:output_dir]      end    end  end diff --git a/lib/tasks/seeds.rake b/lib/tasks/seeds.rake new file mode 100644 index 000000000..9038b64f9 --- /dev/null +++ b/lib/tasks/seeds.rake @@ -0,0 +1,19 @@ +namespace :db do + +  include Seedbank::DSL + +  base_dependencies   = ['db:seed:original'] +  override_dependency = ['db:seed:common'] + +  namespace :seed do +    seeds_environment = ENV.fetch("SEED_ENV", Rails.env) +    glob_seed_files_matching('/*/').each do |directory| +      environment = File.basename(directory) +      override_dependency << "db:seed:#{environment}" if defined?(Rails) && seeds_environment == environment +    end +  end + +  # Override db:seed to run all the common and environments seeds plus the original db:seed. +  desc 'Load the seed data from db/seeds.rb, db/seeds/*.seeds.rb and db/seeds/ENVIRONMENT/*.seeds.rb. ENVIRONMENT is the env var SEED_ENV or the current environment in Rails.env.' +  override_seed_task :seed => override_dependency +end diff --git a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js index 389c60add..608115727 100644 --- a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js +++ b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js @@ -241,6 +241,194 @@ describe('vehicleJourneys reducer', () => {      }, ...state])    }) +  it('should handle ADD_VEHICLEJOURNEY with a start time and a fully timed JP, and use user\'s TZ', () => { +    let pristineVjasList = [{ +      delta : 0, +      arrival_time : { +        hour: 21, +        minute: 54 +      }, +      departure_time : { +        hour: 21, +        minute: 54 +      }, +      stop_point_objectid: 'test-1', +      stop_area_cityname: 'city', +      dummy: false +    }, +    { +      delta : 0, +      arrival_time : { +        hour: 21, +        minute: 57 +      }, +      departure_time : { +        hour: 22, +        minute: 7 +      }, +      stop_point_objectid: 'test-2', +      stop_area_cityname: 'city', +      dummy: false +    }, +    { +      delta : 0, +      arrival_time : { +        hour: "00", +        minute: "00" +      }, +      departure_time : { +        hour: "00", +        minute: "00" +      }, +      stop_point_objectid: 'test-3', +      stop_area_cityname: 'city', +      dummy: true +    }, +    { +      delta : 0, +      arrival_time : { +        hour: 23, +        minute: 37 +      }, +      departure_time : { +        hour: 23, +        minute: 37 +      }, +      stop_point_objectid: 'test-4', +      stop_area_cityname: 'city', +      dummy: false +    }] +    let fakeData = { +      published_journey_name: {value: 'test'}, +      published_journey_identifier: {value : ''}, +      "start_time.hour": {value : '22'}, +      "start_time.minute": {value : '59'}, +      "tz_offset": {value : '-65'} +    } +    let fakeSelectedJourneyPattern = { +      id: "1", +      full_schedule: true, +      stop_areas: [ +        {stop_area_short_description: {id: 1}}, +        {stop_area_short_description: {id: 2}}, +        {stop_area_short_description: {id: 4}}, +      ], +      costs: { +        "1-2": { +          distance: 10, +          time: 63 +        }, +        "2-4": { +          distance: 10, +          time: 30 +        } +      } +    } +    let fakeSelectedCompany = {name: "ALBATRANS"} +    expect( +      vjReducer(state, { +        type: 'ADD_VEHICLEJOURNEY', +        data: fakeData, +        selectedJourneyPattern: fakeSelectedJourneyPattern, +        stopPointsList: [{object_id: 'test-1', city_name: 'city', stop_area_id: 1, id: 1, time_zone_offset: 0, waiting_time: null}, {object_id: 'test-2', city_name: 'city', stop_area_id: 2, id: 2, time_zone_offset: -3600, waiting_time: 10}, {object_id: 'test-3', city_name: 'city', stop_area_id: 3, id: 3, time_zone_offset: 0, waiting_time: 20}, {object_id: 'test-4', city_name: 'city', stop_area_id: 4, id: 4, time_zone_offset: 0}], +        selectedCompany: fakeSelectedCompany +      }) +    ).toEqual([{ +      journey_pattern: fakeSelectedJourneyPattern, +      company: fakeSelectedCompany, +      published_journey_name: 'test', +      published_journey_identifier: '', +      short_id: '', +      objectid: '', +      footnotes: [], +      time_tables: [], +      purchase_windows: [], +      vehicle_journey_at_stops: pristineVjasList, +      selected: false, +      custom_fields: undefined, +      deletable: false, +      transport_mode: 'undefined', +      transport_submode: 'undefined' +    }, ...state]) +  }) + +  it('should handle ADD_VEHICLEJOURNEY with a start time and a fully timed JP but no time is set', () => { +    let pristineVjasList = [{ +      delta : 0, +      arrival_time : { +        hour: 0, +        minute: 0 +      }, +      departure_time : { +        hour: 0, +        minute: 0 +      }, +      stop_point_objectid: 'test-1', +      stop_area_cityname: 'city', +      dummy: false +    }, +    { +      delta : 0, +      arrival_time : { +        hour: 0, +        minute: 0 +      }, +      departure_time : { +        hour: 0, +        minute: 0 +      }, +      stop_point_objectid: 'test-2', +      stop_area_cityname: 'city', +      dummy: false +    }] +    let fakeData = { +      published_journey_name: {value: 'test'}, +      published_journey_identifier: {value : ''}, +      "start_time.hour": {value : ''}, +      "start_time.minute": {value : ''} +    } +    let fakeSelectedJourneyPattern = { +      id: "1", +      full_schedule: true, +      stop_areas: [ +        {stop_area_short_description: {id: 1}}, +        {stop_area_short_description: {id: 2}}, +      ], +      costs: { +        "1-2": { +          distance: 10, +          time: 63 +        }, +      } +    } +    let fakeSelectedCompany = {name: "ALBATRANS"} +    expect( +      vjReducer(state, { +        type: 'ADD_VEHICLEJOURNEY', +        data: fakeData, +        selectedJourneyPattern: fakeSelectedJourneyPattern, +        stopPointsList: [{object_id: 'test-1', city_name: 'city', stop_area_id: 1, id: 1, time_zone_offset: 0}, {object_id: 'test-2', city_name: 'city', stop_area_id: 2, id: 2, time_zone_offset: -3600}], +        selectedCompany: fakeSelectedCompany +      }) +    ).toEqual([{ +      journey_pattern: fakeSelectedJourneyPattern, +      company: fakeSelectedCompany, +      published_journey_name: 'test', +      published_journey_identifier: '', +      short_id: '', +      objectid: '', +      footnotes: [], +      time_tables: [], +      purchase_windows: [], +      vehicle_journey_at_stops: pristineVjasList, +      selected: false, +      custom_fields: undefined, +      deletable: false, +      transport_mode: 'undefined', +      transport_submode: 'undefined' +    }, ...state]) +  }) +    it('should handle ADD_VEHICLEJOURNEY with a start time and a fully timed JP but the minutes are not set', () => {      let pristineVjasList = [{        delta : 0, diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb index 6d699f759..025ad80f9 100644 --- a/spec/models/referential_spec.rb +++ b/spec/models/referential_spec.rb @@ -9,6 +9,24 @@ describe Referential, :type => :model do      subject { build_stubbed(:referential) }      it { should validate_presence_of(:objectid_format) } + +    it "assign slug with a good format" do +      time_reference = double(now: 1234567890) + +      conditions = { +        "2018-Hiver-Jezequel-MM-Lyon-Nice": "hiver_jezequel_mm_lyon_nice_1234567890", +        "2018-Hiver-Jezequel-23293MM-Lyon-Nice": "hiver_jezequel_mm_lyon_nice_1234567890", +        "-Hiver-Jezequel-MM-Lyon-Nice": "hiver_jezequel_mm_lyon_nice_1234567890", +        "Hiver-Jezequel-MM-Lyon-Nice": "hiver_jezequel_mm_lyon_nice_1234567890", +        "20179282": "referential_1234567890" +      } + +      conditions.each do |name, expected_slug| +        ref = Referential.new name: name +        ref.assign_slug time_reference +        expect(ref.slug).to eq(expected_slug) +      end +    end    end    context ".referential_ids_in_periode" do @@ -25,6 +43,16 @@ describe Referential, :type => :model do      end    end +  context "schema creation" do + +    it "should create a schema named as the slug" do +      referential = FactoryGirl.create :referential +      expect(referential.migration_count).to be ActiveRecord::Migrator.get_all_versions.count +      expect(referential.migration_count).to be > 300 +    end + +  end +    context "Cloning referential" do      let(:clone) do        Referential.new_from(ref, []) diff --git a/spec/support/pundit/pundit_view_policy.rb b/spec/support/pundit/pundit_view_policy.rb index 63970de02..316ff6718 100644 --- a/spec/support/pundit/pundit_view_policy.rb +++ b/spec/support/pundit/pundit_view_policy.rb @@ -12,7 +12,7 @@ module Pundit          allow(view).to receive(:current_organisation).and_return(organisation)          allow(view).to receive(:current_offer_workbench).and_return(current_offer_workbench)          allow(view).to receive(:current_workgroup).and_return(current_offer_workbench.workgroup) -        allow(view).to receive(:has_feature?){ |f| features.include?(f)} +        allow(view).to receive(:has_feature?){ |f| respond_to?(:features) && features.include?(f)}          allow(view).to receive(:user_signed_in?).and_return true          allow(view).to receive(:policy) do |instance|            ::Pundit.policy pundit_user, instance | 
