diff options
Diffstat (limited to 'app')
64 files changed, 1120 insertions, 201 deletions
| diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.icoBinary files differ new file mode 100644 index 000000000..7029bd04e --- /dev/null +++ b/app/assets/images/favicon.ico diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass index 3f8467efe..632ade179 100644 --- a/app/assets/stylesheets/application.sass +++ b/app/assets/stylesheets/application.sass @@ -21,3 +21,6 @@  @import 'modules/import_messages'  @import 'flag-icon' + +span.fa + span +  margin-left: 0.2em diff --git a/app/assets/stylesheets/components/_forms.sass b/app/assets/stylesheets/components/_forms.sass index b13c5fc23..e7aa31fab 100644 --- a/app/assets/stylesheets/components/_forms.sass +++ b/app/assets/stylesheets/components/_forms.sass @@ -507,10 +507,10 @@ table, .table      right: 15px      top: 50%      padding: 0 -    margin-top: -13px      z-index: 1 +    transform: translateY(-50%) -    .btn +    *:not(.btn-group) > .btn        color: $blue        font-weight: 700        background-color: transparent @@ -718,6 +718,10 @@ table, .table      > .form-group:last-child        border-right: none +    @for $i from 1 through 99 +      &.w#{$i} +        display: inline-block +  // Form group date  .form-group.date    .form-inline diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass index 3ff0828ea..e22507dcf 100644 --- a/app/assets/stylesheets/modules/_vj_collection.sass +++ b/app/assets/stylesheets/modules/_vj_collection.sass @@ -2,7 +2,7 @@  //  VJ Collection  //  //-----------------// -#vehicle_journeys_wip +#vehicle_journeys_wip, .consolidated-view    .table-2entries      .t2e-head        > .td @@ -218,3 +218,88 @@          // Reset default behaviour          .form-control            border-color: #ccc + + +.consolidated-view +  $highlighted: #d4ba32 +  .line +    & > .head +      font-size: 2em +      text-transform: capitalize +      border-top: 3px solid black +      border-bottom: 3px solid black +      padding-left: 10px +      margin-top: 10px +    .routes +      .route +        background: #F9F9F9 +        & > .head +          &.highlighted +            .pull-right, span.fa +              color: $highlighted !important +          a +            padding: 15px +            color: black +            font-size: 1.2em +            text-transform: capitalize +            display: block +            text-decoration: none +            &:hover +              background-color: #F0F0F0 +            .fa +              color: $red +              transition: transform 0.1s +            &.active .fa +              transform: rotate(180deg) +            .pull-right +              font-size: 0.9em +              text-transform: lowercase +        .vehicle-journeys +          display: block +          overflow: hidden +          transition: height 0.5s, margin-bottom 0.5s +          margin-bottom: 0px +          & > * +            display: inline-block +            & > * +              overflow: hidden +              min-width: 0 +          &.open +            margin-bottom: 40px + +          .highlighted +            background-color: lighten($highlighted, 20%) !important +          .disabled +            color: #bbb +          .t2e-item-list .t2e-item +            .th +              min-width: 100px +              max-width: 150px +              & > div +                text-overflow: ellipsis +                overflow: hidden +                white-space: nowrap +            .td +              text-align: center +              &:hover:after +                position: absolute +                height: 100% +                bottom: 0 +                left: -10000px +                right: -10000px +                content: "" +                background-color: $red +                opacity: 0.1 +                z-index: 10 +              &.headlined:hover:after +                height: 50% + + +  .table-2entries > .t2e-head > .td > div > span::after +    bottom: -6px !important + +  .table.table-2entries .t2e-item-list .t2e-item +    background-color: #F9F9F9 + +  .table.table-2entries .td > div.headlined::before +    border-right: none diff --git a/app/controllers/compliance_check_messages_controller.rb b/app/controllers/compliance_check_messages_controller.rb index 36745981e..db551cca5 100644 --- a/app/controllers/compliance_check_messages_controller.rb +++ b/app/controllers/compliance_check_messages_controller.rb @@ -7,7 +7,7 @@ class ComplianceCheckMessagesController < ChouetteController    def index      index! do |format|        format.csv { -        send_data ComplianceCheckMessageExport.new(compliance_check_messages: collection).to_csv(:col_sep => "\;", :quote_char=>'"', force_quotes: true, server_url: request.base_url) , :filename => "compliance_check_set_errors_#{line_code}_#{Date.today.to_s}.csv" +        send_data ComplianceCheckMessageExport.new(compliance_check_messages: collection).to_csv(:col_sep => "\;", :quote_char=>'"', force_quotes: true, server_url: request.base_url) , :filename => "#{t('compliance_check_messages.compliance_check_set_errors')}_#{line_code}_#{Time.now.strftime('%d-%m-%Y_%H-%M')}.csv"        }      end    end @@ -22,10 +22,10 @@ class ComplianceCheckMessagesController < ChouetteController    end    def compliance_check_resource -    ComplianceCheckResource.find(params[:compliance_check_resource_id])  +    ComplianceCheckResource.find(params[:compliance_check_resource_id])    end -  private  +  private    def line_code      Chouette::Line.find_by_objectid("#{compliance_check_resource.reference}").get_objectid.local_id diff --git a/app/controllers/import_messages_controller.rb b/app/controllers/import_messages_controller.rb index e9a071177..9f61940a3 100644 --- a/app/controllers/import_messages_controller.rb +++ b/app/controllers/import_messages_controller.rb @@ -9,7 +9,7 @@ class ImportMessagesController < ChouetteController    def index      index! do |format|        format.csv { -        send_data Import::MessageExport.new(:import_messages => @import_messages).to_csv(:col_sep => "\;", :quote_char=>'"', force_quotes: true) , :filename => "import_errors_#{@import_resource.name.gsub('.xml', '')}_#{Date.today.to_s}.csv" +        send_data Import::MessageExport.new(:import_messages => @import_messages).to_csv(:col_sep => "\;", :quote_char=>'"', force_quotes: true) , :filename => "#{t('import_messages.import_errors')}_#{@import_resource.name.gsub('.xml', '')}_#{Time.now.strftime("%d-%m-%Y_%H-%M")}.csv"        }      end    end diff --git a/app/controllers/import_resources_controller.rb b/app/controllers/import_resources_controller.rb index 1535fd171..46f8f0337 100644 --- a/app/controllers/import_resources_controller.rb +++ b/app/controllers/import_resources_controller.rb @@ -24,6 +24,15 @@ class ImportResourcesController < ChouetteController      @import_resources ||= parent.resources    end +  def resource +    @import ||= Import::Base.find params[:import_id] +    @import_resource ||= begin +      import_resource = Import::Resource.find params[:id] +      raise ActiveRecord::RecordNotFound unless import_resource.import == @import +      import_resource +    end +  end +    private    def decorate_import_resources(import_resources) diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 8d7a723a0..b98d7da8d 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -4,6 +4,7 @@ class ImportsController < ChouetteController    include IevInterfaces    skip_before_action :authenticate_user!, only: [:download]    defaults resource_class: Import::Base, collection_name: 'imports', instance_name: 'import' +  before_action :notify_parents    def download      if params[:token] == resource.token_download @@ -18,7 +19,7 @@ class ImportsController < ChouetteController    def index_model      Import::Workbench    end -   +    def build_resource      @import ||= Import::Workbench.new(*resource_params) do |import|        import.workbench = parent @@ -43,4 +44,10 @@ class ImportsController < ChouetteController        }      )    end + +  def notify_parents +    if Rails.env.development? +      ParentNotifier.new(Import::Base).notify_when_finished +    end +  end  end diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb index 14f7909b9..111d39c2b 100644 --- a/app/controllers/referential_vehicle_journeys_controller.rb +++ b/app/controllers/referential_vehicle_journeys_controller.rb @@ -42,9 +42,9 @@ class ReferentialVehicleJourneysController < ChouetteController      @q = @q.ransack(params[:q])      @vehicle_journeys ||= @q.result      @vehicle_journeys = parse_order @vehicle_journeys -    @vehicle_journeys = @vehicle_journeys.paginate page: params[:page], per_page: params[:per_page] || 10      @all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct - +    @consolidated = ReferentialConsolidated.new @vehicle_journeys, params +    @vehicle_journeys = @vehicle_journeys.paginate page: params[:page], per_page: params[:per_page] || 10    end    def parse_order scope diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 8addfbc32..80f954cde 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -136,7 +136,6 @@ class ReferentialsController < ChouetteController    def create_resource(referential)      referential.organisation = current_organisation -    referential.ready = true      super    end diff --git a/app/controllers/workbenches_controller.rb b/app/controllers/workbenches_controller.rb index 43415ff60..d4dfdebe3 100644 --- a/app/controllers/workbenches_controller.rb +++ b/app/controllers/workbenches_controller.rb @@ -42,7 +42,7 @@ class WorkbenchesController < ChouetteController    private    def workbench_params -    params.require(:workbench).permit(:import_compliance_control_set_id, :merge_compliance_control_set_id) +    params.require(:workbench).permit(compliance_control_set_ids: @workbench.workgroup.compliance_control_sets_by_workbench.keys)    end    def resource diff --git a/app/controllers/workgroups_controller.rb b/app/controllers/workgroups_controller.rb new file mode 100644 index 000000000..3acea248d --- /dev/null +++ b/app/controllers/workgroups_controller.rb @@ -0,0 +1,13 @@ +class WorkgroupsController < ChouetteController +  defaults resource_class: Workgroup + +  include PolicyChecker + +  def show +    redirect_to "/" +  end + +  def workgroup_params +    params[:workgroup].permit(workbenches_attributes: [:id, compliance_control_set_ids: @workgroup.compliance_control_sets_by_workgroup.keys]) +  end +end diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb index e01987e59..db6261120 100644 --- a/app/decorators/referential_decorator.rb +++ b/app/decorators/referential_decorator.rb @@ -36,7 +36,7 @@ class ReferentialDecorator < AF83::Decorator        l.method :put      end -    instance_decorator.action_link policy: :unarchive, secondary: :show, on: :show do |l| +    instance_decorator.action_link policy: :unarchive, secondary: :show do |l|        l.content t('actions.unarchive')        l.href { h.unarchive_referential_path(object.id) }        l.method :put diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 702ca0ffc..7a3f7e719 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -36,7 +36,7 @@ module ApplicationHelper      display = policy(object).synchronize? if policy(object).respond_to?(:synchronize?) rescue false      if display        info = t('last_update', time: l(object.updated_at, format: :short)) -      if object.has_metadata? +      if object.try(:has_metadata?)          author = object.metadata.modifier_username || t('default_whodunnit')          info   = "#{info} <br/> #{t('whodunnit', author: author)}"        end diff --git a/app/helpers/exports_helper.rb b/app/helpers/exports_helper.rb index 2e784ad35..f30a80ed9 100644 --- a/app/helpers/exports_helper.rb +++ b/app/helpers/exports_helper.rb @@ -17,7 +17,7 @@ module ExportsHelper        message.message_attributes["text"]      else        t([message.class.name.underscore.gsub('/', '_').pluralize, message.message_key].join('.'), message.message_attributes&.symbolize_keys || {}) -    end +    end.html_safe    end    def fields_for_export_task_format(form) diff --git a/app/helpers/imports_helper.rb b/app/helpers/imports_helper.rb index 140660153..f06d77eca 100644 --- a/app/helpers/imports_helper.rb +++ b/app/helpers/imports_helper.rb @@ -2,33 +2,49 @@  module ImportsHelper    # Import statuses helper -  def import_status(status) -    if %w[new running pending].include? status +  def import_status(status, verbose: false, default_status: nil) +    status ||= default_status +    return unless status +    status = status.to_s.downcase +    out = if %w[new running pending].include? status        content_tag :span, '', class: "fa fa-clock-o"      else        cls =''        cls = 'success' if status == 'successful' +      cls = 'success' if status == 'ok'        cls = 'warning' if status == 'warning' -      cls = 'danger' if %w[failed aborted canceled].include? status +      cls = 'danger' if %w[failed aborted canceled error].include? status        content_tag :span, '', class: "fa fa-circle text-#{cls}"      end +    if verbose +      out += content_tag :span do +        txt = "imports.status.#{status}".t(fallback: "") +      end +    end +    out    end    # Compliance check set messages    def bootstrap_class_for_message_criticity message_criticity -    case message_criticity -      when "error" +    case message_criticity.downcase +      when "error", "aborted"          "alert alert-danger"        when "warning"          "alert alert-warning"        when "info"          "alert alert-info" +      when "ok", "success" +        "alert alert-success"        else          message_criticity.to_s      end    end +  def import_message_content message +    export_message_content message +  end +    ##############################    #      TO CLEAN!!!    ############################## diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb index e2aa2e9ea..0b24a9c05 100644 --- a/app/helpers/table_builder_helper.rb +++ b/app/helpers/table_builder_helper.rb @@ -153,7 +153,17 @@ module TableBuilderHelper              i = columns.index(column)              if overhead[i].blank? -              if (i > 0) && (overhead[i - 1][:width] > 1) +              prev = nil +              if i > 0 +                (i-1..0).each do |j| +                  o = overhead[j] +                  if (j + o[:width].to_i) >= i +                    prev = o +                    break +                  end +                end +              end +              if prev                  clsArrayH = overhead[i - 1][:cls].split                  hcont << content_tag(:th, build_column_header( @@ -225,7 +235,7 @@ module TableBuilderHelper          if column.linkable?            path = column.link_to(item) -          link = value.present? && path.present? ? link_to(value, path) : "" +          link = value.present? && path.present? ? link_to(value, path) : value            if overhead.empty?              bcont << content_tag(:td, link, title: 'Voir', class: extra_class) @@ -234,7 +244,17 @@ module TableBuilderHelper              i = columns.index(column)              if overhead[i].blank? -              if (i > 0) && (overhead[i - 1][:width] > 1) +              prev = nil +              if i > 0 +                (i-1..0).each do |j| +                  o = overhead[j] +                  if (j + o[:width].to_i) >= i +                    prev = o +                    break +                  end +                end +              end +              if prev                  clsArrayAlt = overhead[i - 1][:cls].split                  bcont << content_tag(:td, link, title: 'Voir', class: td_cls(clsArrayAlt, extra_class)) diff --git a/app/helpers/vehicle_journeys_helper.rb b/app/helpers/vehicle_journeys_helper.rb index 1cc865c62..4d7eb7002 100644 --- a/app/helpers/vehicle_journeys_helper.rb +++ b/app/helpers/vehicle_journeys_helper.rb @@ -69,4 +69,16 @@ module VehicleJourneysHelper      )    end +  def vehicle_journey_stop_headline prev_sp, sp +    if has_feature?(:long_distance_routes) +      headline = prev_sp && prev_sp.stop_area.country_code +      headline = sp.stop_area.country_code != headline +      headline && sp.stop_area.country_name +    else +      headline = prev_sp && prev_sp.stop_area.city_name +      headline = sp.stop_area.city_name != headline +      headline && sp.stop_area.city_name +    end +  end +  end diff --git a/app/inputs/full_time_zone_input.rb b/app/inputs/full_time_zone_input.rb index f5d8a9ba2..6138d17f5 100644 --- a/app/inputs/full_time_zone_input.rb +++ b/app/inputs/full_time_zone_input.rb @@ -1,7 +1,20 @@  class FullTimeZoneInput < SimpleForm::Inputs::CollectionSelectInput    def collection      @collection ||= begin -      collection = options.delete(:collection) || ActiveSupport::TimeZone::MAPPING +      collection = options.delete(:collection) || begin +        coll = {} + +        TZInfo::Timezone.all_data_zones.map do |tzinfo| +          # v = ActiveSupport::TimeZone.zones_map[k] +        # coll.sort_by do |v| +        #   "(#{v.formatted_offset}) #{v.name}" +        # end +          next if tzinfo.friendly_identifier =~ /^etc/i +          tz = ActiveSupport::TimeZone.new tzinfo.name#, nil, tzinfo +          coll[[tz.utc_offset, tzinfo.friendly_identifier(true)]] = ["(#{tz.formatted_offset}) #{tzinfo.friendly_identifier(true)}", tz.name] +        end +        coll.sort.map(&:last) +      end        collection.respond_to?(:call) ? collection.call : collection.to_a      end    end @@ -9,12 +22,8 @@ class FullTimeZoneInput < SimpleForm::Inputs::CollectionSelectInput    def detect_collection_methods      label, value = options.delete(:label_method), options.delete(:value_method) -    label ||= ->(tz) do -      tz = ActiveSupport::TimeZone[tz.last] -      "(#{tz.formatted_offset}) #{tz.name}" -    end +    label ||= :first      value ||= :last -      [label, value]    end diff --git a/app/javascript/helpers/CustomFieldsInputs.js b/app/javascript/helpers/CustomFieldsInputs.js index 0a57e7566..93611538e 100644 --- a/app/javascript/helpers/CustomFieldsInputs.js +++ b/app/javascript/helpers/CustomFieldsInputs.js @@ -43,7 +43,7 @@ export default class CustomFieldsInputs extends Component {          ref={'custom_fields.' + cf.code}          className='form-control'          disabled={this.props.disabled} -        value={cf.value || this.options(cf).default} +        value={cf.value || this.options(cf).default || ""}          onChange={(e) => {this.props.onUpdate(cf.code, e.target.value); this.forceUpdate()} }          />      ) @@ -56,7 +56,7 @@ export default class CustomFieldsInputs extends Component {          ref={'custom_fields.' + cf.code}          className='form-control'          disabled={this.props.disabled} -        value={cf.value || this.options(cf).default} +        value={cf.value || this.options(cf).default || ""}          onChange={(e) => {this.props.onUpdate(cf.code, e.target.value); this.forceUpdate()} }          />      ) diff --git a/app/javascript/journey_patterns/reducers/journeyPatterns.js b/app/javascript/journey_patterns/reducers/journeyPatterns.js index 1a6a27da6..9dcd82082 100644 --- a/app/javascript/journey_patterns/reducers/journeyPatterns.js +++ b/app/javascript/journey_patterns/reducers/journeyPatterns.js @@ -18,6 +18,7 @@ const journeyPattern = (state = {}, action) =>{          registration_number: action.data.registration_number.value,          stop_points: stopPoints,          costs: {}, +        custom_fields: action.data.custom_fields,          deletable: false        }      case 'UPDATE_CHECKBOX_VALUE': diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index 60496e0ff..98594083d 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -489,23 +489,28 @@ const actions = {      }      return 0    }, -  getDelta: (vjas) => { +  getDelta: (vjas, allowNegative=false) => {      let delta = 0      if (vjas.departure_time.hour != '' && vjas.departure_time.minute != '' && vjas.arrival_time.hour != '' && vjas.departure_time.minute != ''){        delta = (parseInt(vjas.departure_time.hour) - parseInt(vjas.arrival_time.hour)) * 60 + (parseInt(vjas.departure_time.minute) - parseInt(vjas.arrival_time.minute))      } +    if(!true && delta < 0){ +      delta += 24*60 +    }      vjas.delta = delta      return vjas    },    adjustSchedule: (action, schedule, enforceConsistency=false) => {      // we enforce that the departure time remains after the arrival time -    actions.getDelta(schedule) +    actions.getDelta(schedule, true)      if(enforceConsistency && schedule.delta < 0){ -      if(action.isDeparture){ -        schedule.arrival_time = schedule.departure_time -      } -      else{ -        schedule.departure_time = schedule.arrival_time +      if(schedule.arrival_time.hour < 23 || schedule.departure_time.hour > 0){ +        if(action.isDeparture){ +          schedule.arrival_time = schedule.departure_time +        } +        else{ +          schedule.departure_time = schedule.arrival_time +        }        }        actions.getDelta(schedule)      } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index f7ae9341f..46d300e6e 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -192,20 +192,16 @@ export default class VehicleJourney extends Component {                    <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}>                      <input                        type='number' -                      min='00' -                      max='23'                        className='form-control'                        disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}                        readOnly={!this.props.editMode && !vj.dummy}                        onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false)}} -                      onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false, true)}} +                      onBlur={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false, true)}}                        value={vj.arrival_time['hour']}                        />                      <span>:</span>                      <input                        type='number' -                      min='00' -                      max='59'                        className='form-control'                        disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}                        readOnly={!this.props.editMode && !vj.dummy} @@ -225,8 +221,6 @@ export default class VehicleJourney extends Component {                    <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}>                      <input                        type='number' -                      min='00' -                      max='23'                        className='form-control'                        disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}                        readOnly={!this.props.editMode && !vj.dummy} @@ -237,8 +231,6 @@ export default class VehicleJourney extends Component {                      <span>:</span>                      <input                        type='number' -                      min='00' -                      max='59'                        className='form-control'                        disabled={!this.props.editMode || this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false}                        readOnly={!this.props.editMode && !vj.dummy} diff --git a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js index 121be6a00..4931ab46e 100644 --- a/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/javascript/vehicle_journeys/reducers/vehicleJourneys.js @@ -66,10 +66,6 @@ const vehicleJourney= (state = {}, action, keep) => {              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 = { @@ -150,6 +146,16 @@ const vehicleJourney= (state = {}, action, keep) => {        return _.assign({}, state, {vehicle_journey_at_stops: shiftedArray})      case 'UPDATE_TIME':        let vj, vjas, vjasArray, newSchedule +      let val = action.val +      if(val != ''){ +        val = parseInt(val) +        if(action.timeUnit == "minute"){ +          val = (val + 60) % 60 +        } +        else{ +          val = (val + 24) % 24 +        } +      }        vjasArray = state.vehicle_journey_at_stops.map((vjas, i) =>{          if(i == action.subIndex){            newSchedule = { @@ -157,13 +163,13 @@ const vehicleJourney= (state = {}, action, keep) => {              arrival_time: _.assign({}, vjas.arrival_time)            }            if (action.isDeparture){ -            newSchedule.departure_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) +            newSchedule.departure_time[action.timeUnit] = actions.pad(val, action.timeUnit)              if(!action.isArrivalsToggled)                newSchedule.arrival_time[action.timeUnit] = newSchedule.departure_time[action.timeUnit]              newSchedule = actions.adjustSchedule(action, newSchedule, action.enforceConsistency)              return _.assign({}, state.vehicle_journey_at_stops[action.subIndex], {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta})            }else{ -            newSchedule.arrival_time[action.timeUnit] = actions.pad(action.val, action.timeUnit) +            newSchedule.arrival_time[action.timeUnit] = actions.pad(val, action.timeUnit)              newSchedule = actions.adjustSchedule(action, newSchedule, action.enforceConsistency)              return _.assign({}, state.vehicle_journey_at_stops[action.subIndex],  {arrival_time: newSchedule.arrival_time, departure_time: newSchedule.departure_time, delta: newSchedule.delta})            } diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 7a8d043e0..949b18d6f 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -72,6 +72,9 @@ module Chouette        end      end +    has_many :time_tables, :through => :vehicle_journeys +    has_many :purchase_windows, :through => :vehicle_journeys +      accepts_nested_attributes_for :stop_points, :allow_destroy => :true      validates_presence_of :name diff --git a/app/models/compliance_check_set.rb b/app/models/compliance_check_set.rb index 8b1dbdd68..f29ff4de5 100644 --- a/app/models/compliance_check_set.rb +++ b/app/models/compliance_check_set.rb @@ -26,6 +26,10 @@ class ComplianceCheckSet < ApplicationModel      %w(successful failed warning aborted canceled)    end +  def successful? +    status.to_s == "successful" +  end +    def self.abort_old      where(        'created_at < ? AND status NOT IN (?)', @@ -68,6 +72,11 @@ class ComplianceCheckSet < ApplicationModel      end      update attributes +    import_resource&.next_step +  end + +  def import_resource +    referential&.import_resources.main_resources.last    end diff --git a/app/models/concerns/custom_fields_support.rb b/app/models/concerns/custom_fields_support.rb index 6b6621d0c..f5a76f324 100644 --- a/app/models/concerns/custom_fields_support.rb +++ b/app/models/concerns/custom_fields_support.rb @@ -6,7 +6,7 @@ module CustomFieldsSupport      after_initialize :initialize_custom_fields      def self.custom_fields workgroup -      return [] unless workgroup +      return CustomField.none unless workgroup        fields = CustomField.where(resource_type: self.name.split("::").last)        fields = fields.where(workgroup_id: workgroup.id)        fields diff --git a/app/models/concerns/iev_interfaces/resource.rb b/app/models/concerns/iev_interfaces/resource.rb index 7f8c3eefd..254f88a33 100644 --- a/app/models/concerns/iev_interfaces/resource.rb +++ b/app/models/concerns/iev_interfaces/resource.rb @@ -4,6 +4,24 @@ module IevInterfaces::Resource    included do      extend Enumerize      enumerize :status, in: %i(OK ERROR WARNING IGNORED), scope: true -    validates_presence_of :name, :resource_type, :reference +    validates_presence_of :name, :resource_type +  end + +  def update_status_from_importer importer_status +    self.update status: status_from_importer(importer_status) +  end + +  def status_from_importer importer_status +    return nil unless importer_status.present? +    { +      new: nil, +      pending: nil, +      successful: :OK, +      warning: :WARNING, +      failed: :ERROR, +      running: nil, +      aborted: :ERROR, +      canceled: :ERROR +    }[importer_status.to_sym]    end  end diff --git a/app/models/concerns/iev_interfaces/task.rb b/app/models/concerns/iev_interfaces/task.rb index 6be33734b..e40808009 100644 --- a/app/models/concerns/iev_interfaces/task.rb +++ b/app/models/concerns/iev_interfaces/task.rb @@ -31,6 +31,16 @@ module IevInterfaces::Task      before_save :initialize_fields, on: :create      after_save :notify_parent + +    status.values.each do |s| +      define_method "#{s}!" do +        update status: s +      end + +      define_method "#{s}?" do +        status&.to_s == s +      end +    end    end    module ClassMethods @@ -56,13 +66,14 @@ module IevInterfaces::Task    end    def notify_parent -    return unless self.class.finished_statuses.include?(status) +    return false unless self.class.finished_statuses.include?(status) -    return unless parent.present? -    return if notified_parent_at +    return false unless parent.present? +    return false if notified_parent_at      parent.child_change      update_column :notified_parent_at, Time.now +    true    end    def children_succeedeed @@ -94,6 +105,10 @@ module IevInterfaces::Task      update attributes    end +  def successful? +    status.to_s == "successful" +  end +    def child_change      return if self.class.finished_statuses.include?(status)      update_status @@ -112,9 +127,14 @@ module IevInterfaces::Task    def call_boiv_iev      Rails.logger.error("Begin IEV call for import") + +    # Java code expects tasks in NEW status +    # Don't change status before calling iev +      Net::HTTP.get iev_callback_url      Rails.logger.error("End IEV call for import")    rescue Exception => e +    aborted!      logger.error "IEV server error : #{e.message}"      logger.error e.backtrace.inspect    end diff --git a/app/models/import/base.rb b/app/models/import/base.rb index f98e359d4..dcd710e58 100644 --- a/app/models/import/base.rb +++ b/app/models/import/base.rb @@ -32,8 +32,13 @@ class Import::Base < ApplicationModel      Rails.logger.info "update_referentials for #{inspect}"      return unless self.class.finished_statuses.include?(status) -    children.each do |import| -      import.referential.update(ready: true) if import.referential +    # We treat all created referentials in a batch +    # If a single fails, we consider they all failed +    # Ohana means family ! +    if self.successful? +      children.map(&:referential).compact.each &:active! +    else +      children.map(&:referential).compact.each &:failed!      end    end diff --git a/app/models/import/gtfs.rb b/app/models/import/gtfs.rb index ceb849bd8..9dab11f0e 100644 --- a/app/models/import/gtfs.rb +++ b/app/models/import/gtfs.rb @@ -1,19 +1,37 @@  class Import::Gtfs < Import::Base    after_commit :launch_worker, :on => :create +  after_commit do +    main_resource.update_status_from_importer self.status +    true +  end +    def launch_worker      GtfsImportWorker.perform_async id    end +  def main_resource +    @resource ||= parent.resources.find_or_create_by(name: self.name, resource_type: "referential", reference: self.name) if parent +  end + +  def next_step +    main_resource&.next_step +  end + +  def create_message args +    (main_resource || self).messages.build args +  end +    def import      update status: 'running', started_at: Time.now      import_without_status      update status: 'successful', ended_at: Time.now -    referential&.ready! +    referential&.active!    rescue Exception => e      update status: 'failed', ended_at: Time.now      Rails.logger.error "Error in GTFS import: #{e} #{e.backtrace.join('\n')}" +    create_message criticity: :error, message_key: :full_text, message_attributes: {text: e.message}      referential&.failed!    ensure      notify_parent @@ -35,6 +53,7 @@ class Import::Gtfs < Import::Base        workbench_id: workbench.id,        metadatas: [referential_metadata]      ) +    main_resource.update referential: referential if main_resource    end    def referential_metadata @@ -131,17 +150,21 @@ class Import::Gtfs < Import::Base    end    def import_agencies +    count = 0      Chouette::Company.transaction do        source.agencies.each do |agency|          company = line_referential.companies.find_or_initialize_by(registration_number: agency.id)          company.attributes = { name: agency.name }          save_model company +        count += 1        end      end +    create_message criticity: "info", message_key: "gtfs.agencies.imported", message_attributes: {count: count}    end    def import_stops +    count = 0      Chouette::StopArea.transaction do        source.stops.each do |stop|          stop_area = stop_area_referential.stop_areas.find_or_initialize_by(registration_number: stop.id) @@ -155,11 +178,14 @@ class Import::Gtfs < Import::Base          # TODO correct default timezone          save_model stop_area +        count += 1        end +      create_message criticity: "info", message_key: "gtfs.stops.imported", message_attributes: {count: count}      end    end    def import_routes +    count = 0      Chouette::Line.transaction do        source.routes.each do |route|          line = line_referential.lines.find_or_initialize_by(registration_number: route.id) @@ -178,7 +204,9 @@ class Import::Gtfs < Import::Base          line.url = route.url          save_model line +        count += 1        end +      create_message criticity: "info", message_key: "gtfs.routes.imported", message_attributes: {count: count}      end    end @@ -187,6 +215,7 @@ class Import::Gtfs < Import::Base    end    def import_trips +    count = 0      source.trips.each_slice(100) do |slice|        slice.each do |trip|          Chouette::Route.transaction do @@ -205,18 +234,20 @@ class Import::Gtfs < Import::Base            vehicle_journey = journey_pattern.vehicle_journeys.build route: route            vehicle_journey.published_journey_name = trip.headsign.presence || trip.id            save_model vehicle_journey +          count += 1            time_table = referential.time_tables.find_by(id: time_tables_by_service_id[trip.service_id]) if time_tables_by_service_id[trip.service_id]            if time_table              vehicle_journey.time_tables << time_table            else -            messages.create! criticity: "warning", message_key: "gtfs.trips.unkown_service_id", message_attributes: {service_id: trip.service_id} +            create_message criticity: "warning", message_key: "gtfs.trips.unkown_service_id", message_attributes: {service_id: trip.service_id}            end            vehicle_journey_by_trip_id[trip.id] = vehicle_journey.id          end        end      end +    create_message criticity: "info", message_key: "gtfs.trips.imported", message_attributes: {count: count}    end    def import_stop_times @@ -262,6 +293,7 @@ class Import::Gtfs < Import::Base    end    def import_calendars +    count = 0      source.calendars.each_slice(500) do |slice|        Chouette::TimeTable.transaction do          slice.each do |calendar| @@ -272,11 +304,13 @@ class Import::Gtfs < Import::Base            time_table.periods.build period_start: calendar.start_date, period_end: calendar.end_date            save_model time_table +          count += 1            time_tables_by_service_id[calendar.service_id] = time_table.id          end        end      end +    create_message criticity: "info", message_key: "gtfs.calendars.imported", message_attributes: {count: count}    end    def import_calendar_dates @@ -301,10 +335,10 @@ class Import::Gtfs < Import::Base    end    def notify_parent -    return unless parent.present? -    return if notified_parent_at -    parent.child_change -    update_column :notified_parent_at, Time.now +    if super +      main_resource.update_status_from_importer self.status +      next_step +    end    end  end diff --git a/app/models/import/netex.rb b/app/models/import/netex.rb index 49554ee90..b4422328c 100644 --- a/app/models/import/netex.rb +++ b/app/models/import/netex.rb @@ -2,16 +2,39 @@ require 'net/http'  class Import::Netex < Import::Base    before_destroy :destroy_non_ready_referential -  after_commit :call_iev_callback, on: :create +  after_commit do +    main_resource.update_status_from_importer self.status +    true +  end    before_save do -    self.status = 'aborted' unless referential      self.referential&.failed! if self.status == 'aborted' || self.status == 'failed'    end    validates_presence_of :parent +  def main_resource +    @resource ||= parent.resources.find_or_create_by(name: self.name, resource_type: "referential", reference: self.name) +  end + +  def notify_parent +    if super +      main_resource.update_status_from_importer self.status +      next_step +    end +  end + +  def next_step +    main_resource.next_step +  end + +  def create_message args +    main_resource.messages.create args +  end +    def create_with_referential! +    save unless persisted? +      self.referential =        Referential.new(          name: self.name, @@ -20,15 +43,37 @@ class Import::Netex < Import::Base          metadatas: [referential_metadata]        )      self.referential.save -    if self.referential.invalid? + +    if self.referential.valid? +      main_resource.update referential: referential +      call_iev_callback +      save! +    else        Rails.logger.info "Can't create referential for import #{self.id}: #{referential.inspect} #{referential.metadatas.inspect} #{referential.errors.messages}" -      if referential.metadatas.all?{|m| m.line_ids.present? && m.line_ids.empty?} -        parent.messages.create criticity: :error, message_key: "referential_creation_missing_lines", message_attributes: {referential_name: referential.name} + +      if referential.metadatas.all?{|m| m.line_ids.empty? && m.line_ids.empty?} +        create_message criticity: :error, message_key: "referential_creation_missing_lines", message_attributes: {referential_name: referential.name} +      elsif (overlapped_referential_ids = referential.overlapped_referential_ids).any? +        overlapped = Referential.find overlapped_referential_ids.last +        create_message( +          criticity: :error, +          message_key: "referential_creation_overlapping_existing_referential", +          message_attributes: { +            referential_name: referential.name, +            overlapped_name: overlapped.name, +            overlapped_url:  Rails.application.routes.url_helpers.referential_path(overlapped) +          } +        )        else -        parent.messages.create criticity: :error, message_key: "referential_creation", message_attributes: {referential_name: referential.name} +        create_message( +          criticity: :error, +          message_key: "referential_creation", +          message_attributes: {referential_name: referential.name}, +          resource_attributes: referential.errors.messages +        )        end -    else -      save! +      self.referential = nil +      aborted!      end    end @@ -55,6 +100,7 @@ class Import::Netex < Import::Base          metadata.periodes = frame.periods          line_objectids = frame.line_refs.map { |ref| "STIF:CODIFLIGNE:Line:#{ref}" } +        create_message criticity: :info, message_key: "referential_creation_lines_found", message_attributes: {line_objectids: line_objectids.to_sentence}          metadata.line_ids = workbench.lines.where(objectid: line_objectids).pluck(:id)        end      end diff --git a/app/models/import/resource.rb b/app/models/import/resource.rb index 1951daacd..43690755d 100644 --- a/app/models/import/resource.rb +++ b/app/models/import/resource.rb @@ -4,5 +4,49 @@ class Import::Resource < ApplicationModel    include IevInterfaces::Resource    belongs_to :import, class_name: Import::Base +  belongs_to :referential    has_many :messages, class_name: "Import::Message", foreign_key: :resource_id + +  scope :main_resources, ->{ where(resource_type: "referential") } + +  def root_import +    import = self.import +    import = import.parent while import.parent +    import +  end + +  def next_step +    if root_import.class == Import::Workbench + +      return unless netex_import&.successful? + +      workbench.workgroup.import_compliance_control_sets.map do |key, label| +        next unless (control_set = workbench.compliance_control_set(key)).present? +        compliance_check_set = workbench_import_check_set key +        if compliance_check_set.nil? +          ComplianceControlSetCopyWorker.perform_async control_set.id, referential_id, root_import.class.name, root_import.id +        end +      end +    end +  end + +  def workbench +    import.workbench +  end + +  def workgroup +    workbench.workgroup +  end + +  def netex_import +    return unless self.resource_type == "referential" +    import.children.where(name: self.reference).last +  end + +  def workbench_import_check_set key +    return unless referential.present? +    control_set = referential.workbench.compliance_control_set(key) +    return unless control_set.present? +    referential.compliance_check_sets.where(compliance_control_set_id: control_set.id, referential_id: referential_id).last +  end  end diff --git a/app/models/import/workbench.rb b/app/models/import/workbench.rb index 124b9b0d8..95d23fe5b 100644 --- a/app/models/import/workbench.rb +++ b/app/models/import/workbench.rb @@ -9,14 +9,18 @@ class Import::Workbench < Import::Base      end    end +  # def main_resource +  #   @resource ||= resources.find_or_create_by(name: self.name, resource_type: "workbench_import") +  # end +    def import_gtfs      update_column :status, 'running'      update_column :started_at, Time.now -    Import::Gtfs.create! parent_id: self.id, workbench: workbench, file: File.new(file.path), name: "Import GTFS", creator: "Web service" +    Import::Gtfs.create! parent_type: self.class.name, parent_id: self.id, workbench: workbench, file: File.new(file.path), name: "Import GTFS", creator: "Web service" -    update_column :status, 'successful' -    update_column :ended_at, Time.now +    # update_column :status, 'successful' +    # update_column :ended_at, Time.now    rescue Exception => e      Rails.logger.error "Error while processing GTFS file: #{e}" diff --git a/app/models/referential.rb b/app/models/referential.rb index 933bc78e3..0c6e71d47 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -26,6 +26,7 @@ class Referential < ApplicationModel    has_one :user    has_many :api_keys, class_name: 'Api::V1::ApiKey', dependent: :destroy +  has_many :import_resources, class_name: 'Import::Resource', dependent: :destroy    belongs_to :organisation    validates_presence_of :organisation @@ -59,7 +60,6 @@ class Referential < ApplicationModel    belongs_to :referential_suite -    scope :pending, -> { where(ready: false, failed_at: nil, archived_at: nil) }    scope :active, -> { where(ready: true, failed_at: nil, archived_at: nil) }    scope :failed, -> { where.not(failed_at: nil) } @@ -271,7 +271,8 @@ class Referential < ApplicationModel        stop_area_referential: from.stop_area_referential,        created_from: from,        objectid_format: from.objectid_format, -      metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m, organisation) } +      metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m, organisation) }, +      ready: false      )    end @@ -322,6 +323,7 @@ class Referential < ApplicationModel    before_create :create_schema    after_create :clone_schema, if: :created_from +  after_create :active!, unless: :created_from    before_destroy :destroy_schema    before_destroy :destroy_jobs @@ -397,7 +399,7 @@ class Referential < ApplicationModel      query = "select distinct(public.referential_metadata.referential_id) FROM public.referential_metadata, unnest(line_ids) line, LATERAL unnest(periodes) period      WHERE public.referential_metadata.referential_id -    IN (SELECT public.referentials.id FROM public.referentials WHERE referentials.workbench_id = #{workbench_id} and referentials.archived_at is null and referentials.referential_suite_id is null #{not_myself}) +    IN (SELECT public.referentials.id FROM public.referentials WHERE referentials.workbench_id = #{workbench_id} and referentials.archived_at is null and referentials.referential_suite_id is null #{not_myself} AND referentials.failed_at IS NULL)      AND line in (#{line_ids.join(',')}) and (#{periods_query});"      self.class.connection.select_values(query).map(&:to_i) @@ -471,6 +473,7 @@ class Referential < ApplicationModel    end    def destroy_schema +    return unless ActiveRecord::Base.connection.schema_names.include?(slug)      Apartment::Tenant.drop slug    end diff --git a/app/models/workbench.rb b/app/models/workbench.rb index 1c54e8904..1bca91c56 100644 --- a/app/models/workbench.rb +++ b/app/models/workbench.rb @@ -51,6 +51,20 @@ class Workbench < ApplicationModel      where(name: DEFAULT_WORKBENCH_NAME).last    end +  # XXX +  # def import_compliance_control_set +  #   import_compliance_control_set_id && ComplianceControlSet.find(import_compliance_control_set_id) +  # end + +  def compliance_control_set key +    id = (owner_compliance_control_set_ids || {})[key.to_s] +    id.present? && ComplianceControlSet.find(id) +  end + +  def compliance_control_set_ids=(compliance_control_set_ids) +    self.owner_compliance_control_set_ids = (owner_compliance_control_set_ids || {}).merge compliance_control_set_ids +  end +    private    def initialize_output diff --git a/app/models/workgroup.rb b/app/models/workgroup.rb index 3e8409634..6bb03c7fa 100644 --- a/app/models/workgroup.rb +++ b/app/models/workgroup.rb @@ -1,6 +1,7 @@  class Workgroup < ApplicationModel    belongs_to :line_referential    belongs_to :stop_area_referential +  belongs_to :owner, class_name: "Organisation"    has_many :workbenches    has_many :calendars @@ -16,6 +17,8 @@ class Workgroup < ApplicationModel    has_many :custom_fields +  accepts_nested_attributes_for :workbenches +    def custom_fields_definitions      Hash[*custom_fields.map{|cf| [cf.code, cf]}.flatten]    end @@ -23,4 +26,35 @@ class Workgroup < ApplicationModel    def has_export? export_name      export_types.include? export_name    end + +  def all_compliance_control_sets +    %i(after_import +      after_import_by_workgroup +      before_merge +      before_merge_by_workgroup +      after_merge +      after_merge_by_workgroup +      automatic_by_workgroup +    ) +  end + +  def compliance_control_sets_by_workgroup +    compliance_control_sets_labels all_compliance_control_sets.grep(/by_workgroup$/) +  end + +  def compliance_control_sets_by_workbench +    compliance_control_sets_labels all_compliance_control_sets.grep_v(/by_workgroup$/) +  end + +  def import_compliance_control_sets +    compliance_control_sets_labels all_compliance_control_sets.grep(/^after_import/) +  end + +  private +  def compliance_control_sets_labels(keys) +    keys.inject({}) do |h, k| +      h[k] = "workgroups.compliance_control_sets.#{k}".t.capitalize +      h +    end +  end  end diff --git a/app/policies/workbench_policy.rb b/app/policies/workbench_policy.rb index 7b925e91a..9f2279c38 100644 --- a/app/policies/workbench_policy.rb +++ b/app/policies/workbench_policy.rb @@ -6,6 +6,6 @@ class WorkbenchPolicy < ApplicationPolicy    end    def update? -    true +    user.has_permission?('workbenches.update')    end  end diff --git a/app/policies/workgroup_policy.rb b/app/policies/workgroup_policy.rb new file mode 100644 index 000000000..01914bb51 --- /dev/null +++ b/app/policies/workgroup_policy.rb @@ -0,0 +1,19 @@ +class WorkgroupPolicy < ApplicationPolicy +  class Scope < Scope +    def resolve +      scope +    end +  end + +  def create? +    false +  end + +  def desrtroy? +    false +  end + +  def update? +    record.owner == user.organisation +  end +end diff --git a/app/services/referential_consolidated.rb b/app/services/referential_consolidated.rb new file mode 100644 index 000000000..465eab405 --- /dev/null +++ b/app/services/referential_consolidated.rb @@ -0,0 +1,124 @@ +class ReferentialConsolidated +  attr_reader :params + +  def initialize vehicle_journeys, params +    @vehicle_journeys = vehicle_journeys +    @params = params +  end + +  def paginated_lines +    @paginated_lines ||= begin +      line_ids = @vehicle_journeys.joins(route: :line).pluck('lines.id') +      lines = Chouette::Line.where(id: line_ids).order(:name) +      lines.paginate page: params[:page], per_page: params[:per_page] || 10 +    end +  end + +  def lines +    @lines ||= paginated_lines.to_a.map {|l| Line.new(self, l, @vehicle_journeys, params) } +  end + +  def _should_highlight? +    return false unless params[:q].present? +    keys = params[:q].keys - ["stop_areas"] +    params[:q].values_at(*keys).each do |value| +      if value.is_a?(Hash) +        return true if value.values.any?(&:present?) +      elsif value.is_a?(Array) +        return true if value.any?(&:present?) +      else +        if value.present? +          return true +        end +      end +    end +    false +  end + +  def should_highlight? +    if @should_highlight.nil? +      @should_highlight = _should_highlight? +    end +    @should_highlight +  end + +  class Base +    extend Forwardable +    attr_reader :params +    attr_reader :parent +    attr_reader :ar_model + +    def initialize parent, ar_model, vehicle_journeys, params +      @parent = parent +      @ar_model = ar_model +      @all_vehicle_journeys = vehicle_journeys +      @params = params +    end + +    def should_highlight? +      parent.should_highlight? +    end +  end + +  class Line < Base +    delegate name: :ar_model +    delegate id: :ar_model + +    def routes +      @routes ||= begin +        ar_model.routes.order(:name).map {|r| Route.new(self, r, @all_vehicle_journeys, params) } +      end +    end +  end + +  class Route < Base +    def_delegators :ar_model, :name, :id, :time_tables, :purchase_windows, :stop_area_ids + +    def vehicle_journeys +      @vehicle_journeys ||= begin +        ar_model.vehicle_journeys.map {|vj| VehicleJourney.new(self, vj, @all_vehicle_journeys, params) } +      end +    end + +    def highlighted_journeys +      @all_vehicle_journeys.joins(:journey_pattern).where(route_id: self.id) +    end + +    def highlighted_count +      highlighted_journeys.count +    end + +    def highlighted? +      matching_stop_areas = params[:q] && params[:q]["stop_areas"] && (params[:q]["stop_areas"].values & self.stop_area_ids.map(&:to_s)).present? +      (should_highlight? || matching_stop_areas) && highlighted_journeys.exists? +    end + +    def stop_points +      @stop_points ||= ar_model.stop_points.map {|sp| StopPoint.new(self, sp, @all_vehicle_journeys, params) } +    end +  end + +  class VehicleJourney < Base +    def_delegators :ar_model, :id, :published_journey_name, :journey_pattern, :time_tables, :purchase_windows, :vehicle_journey_at_stops, :time_table_ids, :purchase_window_ids, :route + +    def highlighted? +      should_highlight? && @all_vehicle_journeys.where(id: self.id).exists? +    end + +    def has_purchase_window? purchase_window +      purchase_window_ids.include?(purchase_window.id) +    end + +    def has_time_table? time_table +      time_table_ids.include?(time_table.id) +    end +  end + +  class StopPoint < Base +    def_delegators :ar_model, :id, :arrival_time, :departure_time, :name, :stop_area, :stop_area_id + +    def highlighted? +      params[:q] && params[:q]["stop_areas"] && params[:q]["stop_areas"].values.any?{|v| v.to_s == stop_area_id.to_s} +    end +  end +end diff --git a/app/views/companies/edit.html.slim b/app/views/companies/edit.html.slim index faa88f829..c85bef0ec 100644 --- a/app/views/companies/edit.html.slim +++ b/app/views/companies/edit.html.slim @@ -1,3 +1,8 @@  - breadcrumb :company, @company  - page_header_content_for @company -= render 'form'
\ No newline at end of file + +.page_content +  .container-fluid +    .row +      .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 +        = render 'form' diff --git a/app/views/companies/new.html.slim b/app/views/companies/new.html.slim index 1747b8e10..6cc32130d 100644 --- a/app/views/companies/new.html.slim +++ b/app/views/companies/new.html.slim @@ -1,6 +1,7 @@  - breadcrumb :companies, @line_referential +  .page_content    .container-fluid      .row        .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 -        = render 'form'
\ No newline at end of file +        = render 'form' diff --git a/app/views/compliance_check_sets/show.html.slim b/app/views/compliance_check_sets/show.html.slim index 4e1a8e2f9..bf4642b21 100644 --- a/app/views/compliance_check_sets/show.html.slim +++ b/app/views/compliance_check_sets/show.html.slim @@ -9,6 +9,7 @@          = definition_list( t('metadatas'),            { I18n.t("compliance_check_sets.show.metadatas.referential") => (@compliance_check_set.referential.nil? ? '' : link_to(@compliance_check_set.referential.name, referential_path(@compliance_check_set.referential)) ),              I18n.t("compliance_check_sets.show.metadatas.referential_type") => 'Jeu de données', +            I18n.t("compliance_check_sets.show.metadatas.status") => import_status(@compliance_check_set.status, verbose: true),              I18n.t("compliance_check_sets.show.metadatas.compliance_check_set_executed") => link_to(@compliance_check_set.name, executed_workbench_compliance_check_set_path(@compliance_check_set.workbench_id, @compliance_check_set)),              I18n.t("compliance_check_sets.show.metadatas.compliance_control_owner") => @compliance_check_set.organisation.name,              I18n.t("compliance_check_sets.show.metadatas.import") => '' }) diff --git a/app/views/import_resources/show.html.slim b/app/views/import_resources/show.html.slim new file mode 100644 index 000000000..7fd8b4456 --- /dev/null +++ b/app/views/import_resources/show.html.slim @@ -0,0 +1,52 @@ +- breadcrumb :import_resource, @import_resource + +.page_content.import_messages +  .container-fluid +    .row +      .col-lg-12 +        - metadata = { 'Bilan d\'import' => link_to(@import_resource.root_import.name, workbench_import_path(@import_resource.root_import.workbench, @import_resource.root_import) ), +            'Jeu de données associé' => ( @import_resource.referential.present? ? link_to(@import_resource.referential.name, referential_path(@import_resource.referential)) : '-' ) } +        - metadata = metadata.update({t('.status') => import_status(@import_resource.status, verbose: true) }) +        = definition_list t('metadatas'), metadata + + +      .col-lg-12 +        .error_messages +          = render 'shared/iev_interfaces/messages', messages: @import_resource.messages + + +      // XXX +      //- if @import_resource.children.present? +      - if @import_resource&.netex_import&.resources.present? +        .col-lg-12 +          h2 = t('.table_title') +        .col-lg-12 +          = t('.table_explanation') +        .col-lg-12 +          = table_builder_2 @import_resource.netex_import.resources.where(resource_type: :file), +            [ \ +              TableBuilderHelper::Column.new( \ +                key: :name, \ +                attribute: 'name', \ +                sortable: false, \ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :status, \ +                attribute: Proc.new { |n| import_resource_status(n.status) }, \ +                sortable: false, \ +              ), \ +              TableBuilderHelper::Column.new( \ +                name: 'Résultat des tests' , \ +                attribute: Proc.new { |n| I18n.t('import_resources.index.metrics', n.metrics.deep_symbolize_keys) }, \ +                sortable: false, \ +              ), \ +              TableBuilderHelper::Column.new( \ +                name: 'Téléchargement' , \ +                attribute: Proc.new { |n| '<i class="fa fa-download" aria-hidden="true"></i>'.html_safe }, \ +                sortable: false, \ +                link_to: lambda do |import_resource| \ +                  workbench_import_import_resource_import_messages_path(import_resource.import.workbench, import_resource.import, import_resource, format: 'csv' ) \ +                end \ +              ), \ +            ], +            cls: 'table has-search' diff --git a/app/views/imports/import/_gtf.html.slim b/app/views/imports/import/_gtf.html.slim new file mode 100644 index 000000000..8b92f2e92 --- /dev/null +++ b/app/views/imports/import/_gtf.html.slim @@ -0,0 +1,42 @@ +.row +  .col-lg-6.col-md-6.col-sm-12.col-xs-12 +    - metadata = { t('.parent') => link_to(@import.parent.name, [@import.parent.workbench, @import.parent]) } +    - metadata = metadata.update({t('.status') => import_status(@import.status, verbose: true) }) +    - metadata = metadata.update({t('.referential') => @import.referential ? link_to(@import.referential.name, [@import.referential]) : "-" }) +    = definition_list t('metadatas'), metadata + +.col-lg-12 +  .error_messages +    = render 'shared/iev_interfaces/messages', messages: @import.main_resource.messages + +- if @import.resources.any? +  .col-lg-12 +    = table_builder_2 @import.resources, +      [ \ +        TableBuilderHelper::Column.new( \ +          name: t('.referential_name'), \ +          attribute: 'name', \ +          sortable: false, \ +          link_to: lambda do |item| \ +            referential_path(item.referential) if item.referential.present? \ +          end \ +        ), \ +        TableBuilderHelper::Column.new( \ +          key: :status, \ +          attribute: Proc.new { |n| import_status(n.status, verbose: true, default_status: :pending) }, \ +          sortable: false, \ +          link_to: lambda do |item| \ +            item.netex_import.present? ? [@import.workbench, item.netex_import] : [@import.workbench, @import, item] \ +          end \ +        )\ +      ], +      cls: 'table', +      overhead: [ \ +        {}, \ +        {}, \ +        { \ +          title: I18n.t('imports.show.summary').html_safe, \ +          width: controls.size, \ +          cls: 'overheaded-default colspan="2"' \ +        } \ +      ] diff --git a/app/views/imports/import/_netex.html.slim b/app/views/imports/import/_netex.html.slim new file mode 100644 index 000000000..2f341016a --- /dev/null +++ b/app/views/imports/import/_netex.html.slim @@ -0,0 +1,44 @@ +.row +  .col-lg-6.col-md-6.col-sm-12.col-xs-12 +    - metadata = { t('.parent') => link_to(@import.parent.name, [@import.parent.workbench, @import.parent]) } +    - metadata = metadata.update({t('.status') => import_status(@import.status, verbose: true) }) +    - metadata = metadata.update({t('.referential') => @import.referential ? link_to(@import.referential.name, [@import.referential]) : "-" }) +    = definition_list t('metadatas'), metadata + +.col-lg-12 +  .error_messages +    = render 'shared/iev_interfaces/messages', messages: @import.main_resource.messages + +- if @import.resources.present? +  .col-lg-12 +    h2 = t('.table_title') +  .col-lg-12 +    = t('.table_explanation') +  .col-lg-12 +    = table_builder_2 @import.resources.where(resource_type: :file), +      [ \ +        TableBuilderHelper::Column.new( \ +          key: :name, \ +          attribute: 'name', \ +          sortable: false, \ +        ), \ +        TableBuilderHelper::Column.new( \ +          key: :status, \ +          attribute: Proc.new { |n| import_resource_status(n.status) }, \ +          sortable: false, \ +        ), \ +        TableBuilderHelper::Column.new( \ +          name: 'Résultat des tests' , \ +          attribute: Proc.new { |n| I18n.t('import_resources.index.metrics', n.metrics.deep_symbolize_keys) }, \ +          sortable: false, \ +        ), \ +        TableBuilderHelper::Column.new( \ +          name: 'Téléchargement' , \ +          attribute: Proc.new { |n| '<i class="fa fa-download" aria-hidden="true"></i>'.html_safe }, \ +          sortable: false, \ +          link_to: lambda do |import_resource| \ +            workbench_import_import_resource_import_messages_path(import_resource.import.workbench, import_resource.import, import_resource, format: 'csv' ) \ +          end \ +        ), \ +      ], +      cls: 'table has-search' diff --git a/app/views/imports/import/_workbench.html.slim b/app/views/imports/import/_workbench.html.slim new file mode 100644 index 000000000..e41ceb0f0 --- /dev/null +++ b/app/views/imports/import/_workbench.html.slim @@ -0,0 +1,53 @@ +.row +  .col-lg-6.col-md-6.col-sm-12.col-xs-12 +    - metadata = { t('.data_recovery') => '-', t('.filename') => @import.try(:file_identifier)} +    - metadata = metadata.update({t('.status') => import_status(@import.status, verbose: true) }) +    = definition_list t('metadatas'), metadata + +.col-lg-12 +  .error_messages +    = render 'shared/iev_interfaces/messages', messages: @import.messages + +ruby: + controls = @workbench.workgroup.import_compliance_control_sets.map do |key, label| +   TableBuilderHelper::Column.new( +     name: label, +     attribute: Proc.new { |n| n.workbench.compliance_control_set(key).present? ? import_status(n.workbench_import_check_set(key)&.status, verbose: true, default_status: (n.status == "ERROR" ? :aborted : :pending)) : '-' }, +     sortable: false, +     link_to: lambda do |item| +       item.workbench_import_check_set(key).present? && [@import.workbench, item.workbench_import_check_set(key)] +    end +    ) +  end + +- if @import.resources.any? +  .col-lg-12 +    = table_builder_2 @import.resources, +      [ \ +        TableBuilderHelper::Column.new( \ +          name: t('.referential_name'), \ +          attribute: 'name', \ +          sortable: false, \ +          link_to: lambda do |item| \ +            referential_path(item.referential) if item.referential.present? \ +          end \ +        ), \ +        TableBuilderHelper::Column.new( \ +          key: :status, \ +          attribute: Proc.new { |n| import_status(n.netex_import&.status || n.status, verbose: true, default_status: :pending) }, \ +          sortable: false, \ +          link_to: lambda do |item| \ +            item.netex_import.present? ? [@import.workbench, item.netex_import] : [@import.workbench, @import, item] \ +          end \ +        ), *controls \ +      ], +      cls: 'table', +      overhead: [ \ +        {}, \ +        {}, \ +        controls.present? ? { \ +          title: I18n.t('imports.show.summary').html_safe, \ +          width: controls.size, \ +          cls: "overheaded-default colspan='#{controls.size}'" \ +        } : nil \ +      ].compact diff --git a/app/views/imports/show.html.slim b/app/views/imports/show.html.slim index 9d0a6423d..10552129d 100644 --- a/app/views/imports/show.html.slim +++ b/app/views/imports/show.html.slim @@ -4,57 +4,4 @@  .page_content    .container-fluid -    .row -      .col-lg-6.col-md-6.col-sm-12.col-xs-12 -        = definition_list t('metadatas'), { t('.data_recovery') => '-', t('.filename') => @import.try(:file_identifier)} - -    .row -      .col-lg-12 -        .error_messages -          = render 'shared/iev_interfaces/messages', messages: @import.messages - -    - if @import.children.any? -      .row -        .col-lg-12 -          = table_builder_2 @import.children, -            [ \ -              TableBuilderHelper::Column.new( \ -                name: t('.referential_name'), \ -                attribute: 'name', \ -                sortable: false, \ -                link_to: lambda do |import| \ -                  referential_path(import.referential) if import.referential.present? \ -                end \ -              ), \ -              TableBuilderHelper::Column.new( \ -                key: :status, \ -                attribute: Proc.new { |n| import_status(n.status) }, \ -                sortable: false, \ -                link_to: lambda do |import| \ -                  workbench_import_import_resources_path(import.workbench_id, import) \ -                end \ -              ), \ -              TableBuilderHelper::Column.new( \ -                name: t('.stif_control'), \ -                attribute: '', \ -                sortable: false, \ -              ), \ -              TableBuilderHelper::Column.new( \ -                name: t('.organisation_control'), \ -                attribute: '', \ -                sortable: false, \ -              ) \ -            ], -            cls: 'table', -            overhead: [ \ -              {}, \ -              { \ -                title: I18n.t('imports.show.results', count: @import.children_succeedeed, total: @import.children.count), \ -                width: 1, \ -                cls: "#{@import.import_status_css_class} full-border" \ -              }, { \ -                title: I18n.t('imports.show.summary').html_safe, \ -                width: 2, \ -                cls: 'overheaded-default colspan="2"' \ -              } \ -            ] +    = render partial: "imports/#{@import.type.tableize.singularize}" diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index abf39c089..596627996 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -8,6 +8,8 @@ html lang=I18n.locale      title = t('brandname') +    = favicon_link_tag +      = stylesheet_link_tag 'base'      = stylesheet_link_tag 'application' 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 0b55578a7..889f8f944 100644 --- a/app/views/layouts/navigation/_main_nav_left_content.html.slim +++ b/app/views/layouts/navigation/_main_nav_left_content.html.slim @@ -15,6 +15,9 @@              span = t('layouts.navbar.workbench_outputs.organisation')            = link_to '#', class: 'list-group-item disabled' do              span = t('layouts.navbar.workbench_outputs.workgroup') +          - if policy(workbench.workgroup).edit? +            = link_to [:edit, workbench.workgroup], class: 'list-group-item' do +              span = t('layouts.navbar.workbench_outputs.edit_workgroup')      .menu-item.panel        .panel-heading @@ -36,7 +39,10 @@              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 -             +          - if policy(workbench).edit? +            = link_to [:edit, workbench], class: 'list-group-item' do +              span = t('workbenches.edit.link') +      .menu-item.panel        .panel-heading          h4.panel-title 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 9404eeae6..a7bb3f511 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 @@ -14,6 +14,9 @@              span = t('layouts.navbar.workbench_outputs.organisation')            = link_to '#', class: 'list-group-item disabled' do              span = t('layouts.navbar.workbench_outputs.workgroup') +          - if policy(workbench.workgroup).edit? +            = link_to [:edit, workbench.workgroup], class: 'list-group-item' do +              span = t('layouts.navbar.workbench_outputs.edit_workgroup')      .menu-item.panel        .panel-heading @@ -35,6 +38,9 @@              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 +          - if policy(workbench).edit? +            = link_to [:edit, workbench], class: 'list-group-item' do +              span = t('workbenches.edit.link')      .menu-item.panel        .panel-heading diff --git a/app/views/referential_vehicle_journeys/_consolidated.html.slim b/app/views/referential_vehicle_journeys/_consolidated.html.slim new file mode 100644 index 000000000..e2de526bc --- /dev/null +++ b/app/views/referential_vehicle_journeys/_consolidated.html.slim @@ -0,0 +1,33 @@ +.row.consolidated-view +  - @consolidated.lines.each do |line| +    = render partial: "consolidated_line", object: line +  .col-md-12 +    = new_pagination @consolidated.paginated_lines, 'pull-right' + +coffee: +  $('a.toggle-timetables').click (e)-> +    e.preventDefault() +    $(e.target).toggleClass 'active' +    $(e.target).parents('.table').find('.detailed-timetables').toggleClass 'hidden' + +  $('a.toggle-purchase-windows').click (e)-> +    e.preventDefault() +    $(e.target).toggleClass 'active' +    $(e.target).parents('.table').find('.detailed-purchase-windows').toggleClass 'hidden' + +  $('.route').find('.vehicle-journeys').each (i, e)-> +    $e = $(e) +    $e.removeClass 'hidden' +    e.setAttribute 'data-original-height', $e.height() +    $e.height 0 + +  $('a.toggle-route').click (e)-> +    e.preventDefault() +    $(e.currentTarget).toggleClass 'active' +    tab = $(e.currentTarget).parents('.route').find('.vehicle-journeys') +    if tab.hasClass 'open' +      tab.removeClass 'open' +      tab.height 0 +    else +      tab.addClass 'open' +      tab.height tab.attr('data-original-height') diff --git a/app/views/referential_vehicle_journeys/_consolidated_line.html.slim b/app/views/referential_vehicle_journeys/_consolidated_line.html.slim new file mode 100644 index 000000000..d4c756d38 --- /dev/null +++ b/app/views/referential_vehicle_journeys/_consolidated_line.html.slim @@ -0,0 +1,119 @@ +.line.col-md-12 +  .head +    span +      = Chouette::Line.ts +      |   +    strong= consolidated_line.name +  .routes +    - consolidated_line.routes.each do |route| +      .route +        .head class="#{route.highlighted? ? 'highlighted' : ''}" +          = link_to '#', class: 'toggle-route' do +            span.sb.sb-route +            |   +            = Chouette::Route.ts +            |   +            strong= route.name +            .pull-right +              = route.highlighted_count +              |   +              = Chouette::VehicleJourney.t +              |   +              span.fa.fa-angle-up +        .table.table-2entries.vehicle-journeys.hidden +          .t2e-head.w20 +            .th +              div +                strong= Chouette::VehicleJourney.tmf(:id) +              div= Chouette::VehicleJourney.tmf(:name) +              div= Chouette::VehicleJourney.tmf(:journey_pattern_id) +              div +                = link_to '#', class: 'toggle-purchase-windows detailed-timetables-bt' do +                  span.fa.fa-angle-up +                  = Chouette::PurchaseWindow.t +              .detailed-purchase-windows.hidden +                - route.purchase_windows.uniq.each do |tt| +                  div +                    p +                      = link_to [@referential, tt] do +                        span.fa.fa-calendar style={color: (tt.color ? tt.color : '#4B4B4B')} +                        |   +                        = tt.name + +                    p= tt.bounding_dates.split(' ').join(' > ') +              div +                = link_to '#', class: 'toggle-timetables detailed-timetables-bt' do +                  span.fa.fa-angle-up +                  = Chouette::TimeTable.t + +              .detailed-timetables.hidden +                - route.time_tables.uniq.each do |tt| +                  div +                    p +                      = link_to [@referential, tt] do +                        span.fa.fa-calendar style={color: (tt.color ? tt.color : '#4B4B4B')} +                        |   +                        = tt.display_day_types + +                    p= tt.bounding_dates.split(' ').join(' > ') +            - prev_sp = nil +            - route.stop_points.each do |sp| +              ruby: +                headline = vehicle_journey_stop_headline prev_sp, sp +                prev_sp = sp +              .td class="#{sp.highlighted? ? 'highlighted' : ''}" +                div title="#{sp.stop_area.city_name ? "#{sp.stop_area.city_name} (#{sp.stop_area.zip_code})" : ''}" data-headline=headline class=(headline.present? ? 'headlined' : '') +                  span +                    = sp.name +                    - if sp.stop_area.time_zone_formatted_offset +                      span.small +                       |   +                       = "(#{sp.stop_area.time_zone_formatted_offset})" +          .t2e-item-list.w80 +            div +              - route.vehicle_journeys.each do |journey| +                .t2e-item class="#{journey.highlighted? ? 'highlighted' : ''}" +                  .th +                    div +                      strong= link_to journey.id, [@referential, journey.route.line, journey.route, :vehicle_journeys] +                    div +                      = link_to journey.published_journey_name, [@referential, journey.route.line, journey.route, :vehicle_journeys], title: journey.published_journey_name +                    div= journey.journey_pattern.get_objectid.short_id +                    div +                      - journey.purchase_windows[0..3].each do |tt| +                        span.vj_tt +                          = link_to [@referential, tt], target: :blank do +                            span.fa.fa-calendar style="color: #{tt.color ? tt.color : '#4B4B4B'}" +                      - if journey.purchase_windows.size > 3 +                        span.vj_tt = "+ #{journey.purchase_windows.size - 3}" +                    .detailed-purchase-windows.hidden +                      - route.purchase_windows.uniq.each do |tt| +                        div class=(journey.has_purchase_window?(tt) ? 'active' : 'inactive') +                    div +                      - journey.time_tables[0..3].each do |tt| +                        span.vj_tt +                          = link_to [@referential, tt], target: :blank do +                            span.fa.fa-calendar style="color: #{tt.color ? tt.color : '#4B4B4B'}" +                      - if journey.time_tables.size > 3 +                        span.vj_tt = "+ #{journey.time_tables.size - 3}" +                    .detailed-timetables.hidden +                      - route.time_tables.uniq.each do |tt| +                        div class=(journey.has_time_table?(tt) ? 'active' : 'inactive') + +                  - prev_sp = nil +                  - route.stop_points.each do |sp| +                    ruby: +                      headline = vehicle_journey_stop_headline prev_sp, sp +                      prev_sp = sp +                      vjas = journey.vehicle_journey_at_stops.where(stop_point_id: sp.id).last +                    .td class="#{vjas && sp.highlighted? ? 'highlighted' : ''} #{vjas.nil? ? 'disabled' : ''} #{headline.present? ? 'headlined' : ''}" +                      div title="#{sp.stop_area.city_name ? "#{sp.stop_area.city_name} (#{sp.stop_area.zip_code})" : ''}" data-headline=headline class=(headline.present? ? 'headlined' : '') +                        - if vjas.present? +                          - if vjas.departure_time == vjas.arrival_time +                            = vjas.departure_time.l(format: "%H:%M") +                          - else +                            = vjas.arrival_time.l(format: "%H:%M") +                            |  -  +                            = vjas.departure_time.l(format: "%H:%M") +                        - else +                          | 00:00 diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim index a6e289b97..31053c5ba 100644 --- a/app/views/referential_vehicle_journeys/_filters.html.slim +++ b/app/views/referential_vehicle_journeys/_filters.html.slim @@ -1,10 +1,20 @@  = search_form_for @q, url: referential_vehicle_journeys_path(@referential), html: {method: :get}, class: 'form form-filter' do |f| -  .ffg-row +  input type="hidden" name="display" value=params[:display] +  .ffg-row.w85      .input-group.search_bar        = f.search_field :published_journey_name_or_objectid_cont, placeholder: t('.published_journey_name_or_objectid'), class: 'form-control'        span.input-group-btn          button.btn.btn-default#search-btn type='submit'            span.fa.fa-search +  .ffg-row.w15 +    - if has_feature?(:consolidated_offers) +      .form-group +        .btn-group +          = link_to referential_vehicle_journeys_path(@referential, q: params[:q], display: :list), class: 'btn btn-default ' + (params[:display] != "consolidated" ? 'active' : '') do +            span.fa.fa-align-justify +          = link_to referential_vehicle_journeys_path(@referential, q: params[:q], display: :consolidated), class: 'btn btn-default ' + (params[:display] == "consolidated" ? 'active' : '') do +            span.fa.fa-th-large +    .ffg-row      .form-group.per-page-select        = I18n.t("simple_form.per_page") @@ -73,5 +83,5 @@    .actions -    = link_to t('actions.erase'), referential_vehicle_journeys_path(@referential), class: 'btn btn-link' +    = link_to t('actions.erase'), referential_vehicle_journeys_path(@referential, display: params[:display]), class: 'btn btn-link'      = f.submit t('actions.filter'), class: 'btn btn-default' diff --git a/app/views/referential_vehicle_journeys/_list.html.slim b/app/views/referential_vehicle_journeys/_list.html.slim new file mode 100644 index 000000000..74f8238f8 --- /dev/null +++ b/app/views/referential_vehicle_journeys/_list.html.slim @@ -0,0 +1,49 @@ +.row +  .col-lg-12 +    .select_table +      = table_builder_2 @vehicle_journeys, +        [ \ +          TableBuilderHelper::Column.new( \ +            name: t('objectid'), \ +            attribute: Proc.new { |n| n.get_objectid.short_id }, \ +            sortable: false \ +          ), \ +          TableBuilderHelper::Column.new( \ +            key: :published_journey_name, \ +            attribute: 'published_journey_name', \ +            link_to: lambda do |vehicle_journey| \ +              vehicle_journey.published_journey_name ? referential_line_route_vehicle_journeys_path(@referential, vehicle_journey.route.line, vehicle_journey.route) : '' \ +            end, \ +            sortable: true \ +          ), \ +          TableBuilderHelper::Column.new( \ +            key: :line, \ +            attribute: Proc.new {|v| v.route.line.name}, \ +            sortable: true \ +          ), \ +          TableBuilderHelper::Column.new( \ +            key: :route, \ +            attribute: Proc.new {|v| v.route.name}, \ +            sortable: true \ +          ), \ +          TableBuilderHelper::Column.new( \ +            key: :departure_time, \ +            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_local }, \ +            sortable: true, \ +          ), \ +        ].flatten.compact, +        cls: 'table has-filter has-search' + +    = new_pagination @vehicle_journeys, 'pull-right' diff --git a/app/views/referential_vehicle_journeys/index.html.slim b/app/views/referential_vehicle_journeys/index.html.slim index 00f63cb65..d1d1dae07 100644 --- a/app/views/referential_vehicle_journeys/index.html.slim +++ b/app/views/referential_vehicle_journeys/index.html.slim @@ -9,55 +9,11 @@            = render 'filters'      - if @vehicle_journeys.present? -      .row -        .col-lg-12 -          .select_table -            = table_builder_2 @vehicle_journeys, -              [ \ -                TableBuilderHelper::Column.new( \ -                  name: t('objectid'), \ -                  attribute: Proc.new { |n| n.get_objectid.short_id }, \ -                  sortable: false \ -                ), \ -                TableBuilderHelper::Column.new( \ -                  key: :published_journey_name, \ -                  attribute: 'published_journey_name', \ -                  link_to: lambda do |vehicle_journey| \ -                    vehicle_journey.published_journey_name ? referential_line_route_vehicle_journeys_path(@referential, vehicle_journey.route.line, vehicle_journey.route) : '' \ -                  end, \ -                  sortable: true \ -                ), \ -                TableBuilderHelper::Column.new( \ -                  key: :line, \ -                  attribute: Proc.new {|v| v.route.line.name}, \ -                  sortable: true \ -                ), \ -                TableBuilderHelper::Column.new( \ -                  key: :route, \ -                  attribute: Proc.new {|v| v.route.name}, \ -                  sortable: true \ -                ), \ -                TableBuilderHelper::Column.new( \ -                  key: :departure_time, \ -                  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_local }, \ -                  sortable: true, \ -                ), \ -              ].flatten.compact, -              cls: 'table has-filter has-search' +      - if params[:display] == "consolidated" && has_feature?(:consolidated_offers) +        = render partial: "consolidated" +      - else +        = render partial: "list" -          = new_pagination @vehicle_journeys, 'pull-right'      - unless @vehicle_journeys.any?        .row.mt-xs diff --git a/app/views/referentials/_filters.html.slim b/app/views/referentials/_filters.html.slim index 36db5bfb5..ebaefb0f2 100644 --- a/app/views/referentials/_filters.html.slim +++ b/app/views/referentials/_filters.html.slim @@ -14,12 +14,12 @@      - if (network_ids = @referential.lines.pluck(:network_id).uniq.compact).size > 1        .form-group.togglable class=filter_item_class(params[:q], :network_id_eq_any)          = f.label  t('activerecord.attributes.referential.networks'), required: false, class: 'control-label' -        = f.input :network_id_eq_any, collection: network_ids, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.networks.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' } +        = f.input :network_id_eq_any, collection: network_ids, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{Chouette::Network.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }      - if (company_ids = @referential.lines.pluck(:company_id).uniq.compact).size > 1        .form-group.togglable class=filter_item_class(params[:q], :company_id_eq_any)          = f.label t('activerecord.attributes.referential.companies'), required: false, class: 'control-label' -        = f.input :company_id_eq_any, collection: company_ids, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{LineReferential.first.companies.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' } +        = f.input :company_id_eq_any, collection: company_ids, as: :check_boxes, label: false, label_method: lambda{|l| ("<span>#{Chouette::Company.find(l).name}</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list' }    .actions      = link_to t('actions.erase'), @workbench, class: 'btn btn-link' diff --git a/app/views/shared/_development_toolbar.html.slim b/app/views/shared/_development_toolbar.html.slim index 836066b3d..1d45c41d0 100644 --- a/app/views/shared/_development_toolbar.html.slim +++ b/app/views/shared/_development_toolbar.html.slim @@ -1,4 +1,4 @@ -- if Rails.application.config.development_toolbar +- if Rails.application.config.development_toolbar && current_user    = modalbox 'development-toolbar' do      = form_tag development_toolbar_update_settings_path, authenticity_token: true do        .modal-header diff --git a/app/views/workbenches/_form.html.slim b/app/views/workbenches/_form.html.slim index 534a5f378..819346c35 100644 --- a/app/views/workbenches/_form.html.slim +++ b/app/views/workbenches/_form.html.slim @@ -1,9 +1,8 @@  = simple_form_for @workbench, html: { class: 'form-horizontal', id: 'workbench_form' }, wrapper: :horizontal_form do |f|    .row      .col-lg-12 -      = f.input :import_compliance_control_set_id, as: :select, collection: current_organisation.compliance_control_sets, value_method: :id -      = f.input :merge_compliance_control_set_id, as: :select, collection: current_organisation.compliance_control_sets, value_method: :id - -  .separator +      = f.fields_for :compliance_control_set_ids do |ff| +        - @workbench.workgroup.compliance_control_sets_by_workbench.each do |cc, label| +          = ff.input cc, as: :select, collection: current_organisation.compliance_control_sets, value_method: :id, label: label, selected: @workbench.compliance_control_set(cc).try(:id).try(:to_s), include_blank: true    = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'workbench_form' diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index b0276c5ce..213c9e5f2 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -62,7 +62,10 @@                    attribute: Proc.new {|w| w.merged_at ? l(w.merged_at, format: :short) : '-'} \                  ) \                ], -              selectable: ->(ref){ @workbench.referentials.include?(ref) }, +              selectable: ->(ref) { \ +                @workbench.referentials.include?(ref) && \ +                  !ref.pending? \ +              },                cls: 'table has-filter has-search',                action: :index diff --git a/app/views/workgroups/_form.html.slim b/app/views/workgroups/_form.html.slim new file mode 100644 index 000000000..7245cfc40 --- /dev/null +++ b/app/views/workgroups/_form.html.slim @@ -0,0 +1,15 @@ += simple_form_for @workgroup, html: { class: 'form-horizontal', id: 'workgroup_form' }, wrapper: :horizontal_form do |f| +  table.table +    thead +      th +        - @workgroup.compliance_control_sets_by_workgroup.values.each do |cc| +          th= cc +    - @workgroup.workbenches.each_with_index do |w,i| +      tr +        th= w.organisation.name +        - @workgroup.compliance_control_sets_by_workgroup.keys.each do |cc| +          td +            = hidden_field_tag "workgroup[workbenches_attributes][#{i}][id]", w.id +            = select_tag "workgroup[workbenches_attributes][#{i}][compliance_control_set_ids][#{cc}]", options_from_collection_for_select(current_organisation.compliance_control_sets, :id, :name, w.compliance_control_set(cc).try(:id)), include_blank: true + +  = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'workgroup_form' diff --git a/app/views/workgroups/edit.html.slim b/app/views/workgroups/edit.html.slim new file mode 100644 index 000000000..49847acf2 --- /dev/null +++ b/app/views/workgroups/edit.html.slim @@ -0,0 +1,8 @@ +- breadcrumb @workgroup +- page_header_content_for @workgroup + +.page_content + .container-fluid +   .row +     .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 +       == render 'form' diff --git a/app/workers/compliance_control_set_copy_worker.rb b/app/workers/compliance_control_set_copy_worker.rb index d18bb0c88..b87f5ad8e 100644 --- a/app/workers/compliance_control_set_copy_worker.rb +++ b/app/workers/compliance_control_set_copy_worker.rb @@ -1,8 +1,9 @@  class ComplianceControlSetCopyWorker    include Sidekiq::Worker -  def perform(control_set_id, referential_id) +  def perform(control_set_id, referential_id, parent_type = nil, parent_id = nil)      check_set = ComplianceControlSetCopier.new.copy(control_set_id, referential_id) +    check_set.update parent_type: parent_type, parent_id: parent_id if parent_type && parent_id      begin        Net::HTTP.get(URI("#{Rails.configuration.iev_url}/boiv_iev/referentials/validator/new?id=#{check_set.id}")) diff --git a/app/workers/workbench_import_worker/object_state_updater.rb b/app/workers/workbench_import_worker/object_state_updater.rb index 1edc6b9a1..39d5f20b1 100644 --- a/app/workers/workbench_import_worker/object_state_updater.rb +++ b/app/workers/workbench_import_worker/object_state_updater.rb @@ -2,6 +2,11 @@  class WorkbenchImportWorker    module ObjectStateUpdater +    def resource entry +      @_resources ||= {} +      @_resources[entry.name] ||= workbench_import.resources.find_or_create_by(name: entry.name, resource_type: "referential") +    end +      def update_object_state entry, count        workbench_import.update( total_steps: count )        update_spurious entry @@ -14,44 +19,48 @@ class WorkbenchImportWorker      def update_foreign_lines entry        return if entry.foreign_lines.empty? -      workbench_import.messages.create( +      resource(entry).messages.create(          criticity: :error,          message_key: 'foreign_lines_in_referential',          message_attributes: {            'source_filename' => workbench_import.file.file.file,            'foreign_lines'   => entry.foreign_lines.join(', ')          }) +      resource(entry).update status: :ERROR      end      def update_spurious entry        return if entry.spurious.empty? -      workbench_import.messages.create( +      resource(entry).messages.create(          criticity: :error,          message_key: 'inconsistent_zip_file',          message_attributes: {            'source_filename' => workbench_import.file.file.file,            'spurious_dirs'   => entry.spurious.join(', ')          }) +      resource(entry).update status: :ERROR      end      def update_missing_calendar entry        return unless entry.missing_calendar -      workbench_import.messages.create( +      resource(entry).messages.create(          criticity: :error,          message_key: 'missing_calendar_in_zip_file',          message_attributes: {            'source_filename' => entry.name          }) +      resource(entry).update status: :ERROR      end      def update_wrong_calendar entry        return unless entry.wrong_calendar -      workbench_import.messages.create( +      resource(entry).messages.create(          criticity: :error,          message_key: 'wrong_calendar_in_zip_file',          message_attributes: {            'source_filename' => entry.name          }) +      resource(entry).update status: :ERROR      end    end  end | 
