diff options
| author | Alban Peignier | 2018-03-15 18:27:41 +0100 | 
|---|---|---|
| committer | GitHub | 2018-03-15 18:27:41 +0100 | 
| commit | 90ae17c0d0f626763f456f061c472a66ec06eef1 (patch) | |
| tree | fb97b63dc1f7c8be98b5cbce40c06ce693641b45 | |
| parent | f011f7e9806ffeaaba3ad73510bc818211f55dbd (diff) | |
| parent | dd8b4cc13d8e052d0fac7a25569f869470198256 (diff) | |
| download | chouette-core-90ae17c0d0f626763f456f061c472a66ec06eef1.tar.bz2 | |
Merge pull request #385 from af83/6203-journeypatternscollection-show--use-route-WayCosts-in-c
JourneyPatternsCollection#show use route way costs as default cost. Refs #6203
14 files changed, 211 insertions, 8 deletions
| diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb index af5a9a91b..418ba3751 100644 --- a/app/controllers/routes_controller.rb +++ b/app/controllers/routes_controller.rb @@ -70,6 +70,10 @@ class RoutesController < ChouetteController      redirect_to referential_line_path(@referential, route.line)    end +  def costs +    @route = resource +  end +    protected    alias_method :route, :resource diff --git a/app/javascript/journey_patterns/actions/index.js b/app/javascript/journey_patterns/actions/index.js index a70a2e6f2..b90908264 100644 --- a/app/javascript/journey_patterns/actions/index.js +++ b/app/javascript/journey_patterns/actions/index.js @@ -20,6 +20,12 @@ const actions = {      type: "RECEIVE_ERRORS",      json    }), +  receiveRouteCosts: (costs, key, index) => ({ +    type: "RECEIVE_ROUTE_COSTS", +    costs, +    key, +    index +  }),    unavailableServer : () => ({      type: 'UNAVAILABLE_SERVER'    }), @@ -213,6 +219,30 @@ const actions = {          }        })    }, +  fetchRouteCosts: (dispatch, key, index) => { +    if (actions.routeCostsCache) { +      // Dispatch asynchronously to prevent warning when +      // this executes during `render()` +      requestAnimationFrame(() => { +        dispatch(actions.receiveRouteCosts( +          actions.routeCostsCache, +          key, +          index +        )) +      }) +    } else { +      fetch(window.routeCostsUrl, { +        credentials: 'same-origin', +      }).then(response => { +        return response.json() +      }).then(json => { +        let costs = json.costs ? json.costs : {} +        actions.routeCostsCache = costs + +        dispatch(actions.receiveRouteCosts(costs, key, index)) +      }) +    } +  },    getChecked : (jp) => {      return jp.filter((obj) => {        return obj.checked diff --git a/app/javascript/journey_patterns/components/JourneyPattern.js b/app/javascript/journey_patterns/components/JourneyPattern.js index d20294594..15d8b6db4 100644 --- a/app/javascript/journey_patterns/components/JourneyPattern.js +++ b/app/javascript/journey_patterns/components/JourneyPattern.js @@ -98,12 +98,24 @@ export default class JourneyPattern extends Component{    getTimeAndDistanceBetweenStops(from, to){      let costsKey = from + "-" + to -    let costs = this.props.value.costs[costsKey] || {distance: 0, time: 0} +    let costs = this.getCosts(costsKey)      let time = costs['time'] || 0      let distance = costs['distance'] || 0      return [costsKey, costs, time, distance]    } +  getCosts(costsKey) { +    let cost = this.props.value.costs[costsKey] + +    if (cost) { +      return cost +    } + +    this.props.fetchRouteCosts(costsKey) + +    return { distance: 0, time: 0 } +  } +    formatDistance(distance){      return parseFloat(Math.round(distance * 100) / 100).toFixed(2) + " km"    } @@ -119,9 +131,12 @@ export default class JourneyPattern extends Component{      }    } +  componentWillUpdate() { +    [this.totalTime, this.totalDistance] = this.totals(false) +  } +    render() {      this.previousSpId = undefined -    let [totalTime, totalDistance] = this.totals(false)      let [commercialTotalTime, commercialTotalDistance] = this.totals(true)      return (        <div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.object_id ? '' : ' to_record') + (this.props.value.errors ? ' has-error': '') + (this.hasFeature('costs_in_journey_patterns') ? ' with-costs' : '')}> @@ -131,8 +146,8 @@ export default class JourneyPattern extends Component{            <div>{actions.getChecked(this.props.value.stop_points).length} arrĂȘt(s)</div>            {this.hasFeature('costs_in_journey_patterns') &&              <div className="small row totals"> -              <span className="col-md-6"><i className="fa fa-arrows-h"></i>{totalDistance}</span> -              <span className="col-md-6"><i className="fa fa-clock-o"></i>{totalTime}</span> +              <span className="col-md-6"><i className="fa fa-arrows-h"></i>{this.totalDistance}</span> +              <span className="col-md-6"><i className="fa fa-clock-o"></i>{this.totalTime}</span>              </div>            }            {this.hasFeature('costs_in_journey_patterns') && @@ -228,5 +243,6 @@ JourneyPattern.propTypes = {    onCheckboxChange: PropTypes.func.isRequired,    onOpenEditModal: PropTypes.func.isRequired,    onDeleteJourneyPattern: PropTypes.func.isRequired, -  journeyPatterns: PropTypes.object.isRequired +  journeyPatterns: PropTypes.object.isRequired, +  fetchRouteCosts: PropTypes.func.isRequired  } diff --git a/app/javascript/journey_patterns/components/JourneyPatterns.js b/app/javascript/journey_patterns/components/JourneyPatterns.js index e8b6bf143..930acb390 100644 --- a/app/javascript/journey_patterns/components/JourneyPatterns.js +++ b/app/javascript/journey_patterns/components/JourneyPatterns.js @@ -138,6 +138,7 @@ export default class JourneyPatterns extends Component {                        status= {this.props.status}                        editMode= {this.props.editMode}                        journeyPatterns= {this} +                      fetchRouteCosts={(costsKey) => this.props.fetchRouteCosts(costsKey, index)}                        />                    )}                  </div> @@ -156,5 +157,6 @@ JourneyPatterns.propTypes = {    status: PropTypes.object.isRequired,    onCheckboxChange: PropTypes.func.isRequired,    onLoadFirstPage: PropTypes.func.isRequired, -  onOpenEditModal: PropTypes.func.isRequired +  onOpenEditModal: PropTypes.func.isRequired, +  fetchRouteCosts: PropTypes.func.isRequired  } diff --git a/app/javascript/journey_patterns/containers/JourneyPatternList.js b/app/javascript/journey_patterns/containers/JourneyPatternList.js index d338345f2..539b6b54c 100644 --- a/app/javascript/journey_patterns/containers/JourneyPatternList.js +++ b/app/javascript/journey_patterns/containers/JourneyPatternList.js @@ -29,6 +29,9 @@ const mapDispatchToProps = (dispatch) => {      onUpdateJourneyPatternCosts: (index, costs) =>{        dispatch(actions.updateJourneyPatternCosts(index, costs))      }, +    fetchRouteCosts: (key, index) => { +      actions.fetchRouteCosts(dispatch, key, index) +    },    }  } diff --git a/app/javascript/journey_patterns/reducers/journeyPatterns.js b/app/javascript/journey_patterns/reducers/journeyPatterns.js index 1ce069522..6c38e9288 100644 --- a/app/javascript/journey_patterns/reducers/journeyPatterns.js +++ b/app/javascript/journey_patterns/reducers/journeyPatterns.js @@ -40,6 +40,17 @@ export default function journeyPatterns (state = [], action)  {        return [...action.json]      case 'RECEIVE_ERRORS':        return [...action.json] +    case 'RECEIVE_ROUTE_COSTS': +      return state.map((j, i) =>{ +        if(i == action.index) { +          const new_costs = Object.assign({}, j.costs) +          new_costs[action.key] = action.costs[action.key] || +            {distance: 0, time: 0} +          return _.assign({}, j, {costs: new_costs}) +        } else { +          return j +        } +      })      case 'GO_TO_PREVIOUS_PAGE':        $('#ConfirmModal').modal('hide')        if(action.pagination.page > 1){ diff --git a/app/views/journey_patterns_collections/show.html.slim b/app/views/journey_patterns_collections/show.html.slim index 8a7b0c47c..b389a1da7 100644 --- a/app/views/journey_patterns_collections/show.html.slim +++ b/app/views/journey_patterns_collections/show.html.slim @@ -19,5 +19,6 @@    | window.journeyPatternsPerPage = #{@ppage};    | window.perms = #{raw @perms};    | window.features = #{raw @features}; +  | window.routeCostsUrl = "#{costs_referential_line_route_url(@referential, @route.line, @route, format: :json).html_safe}";  = javascript_pack_tag 'journey_patterns/index.js' diff --git a/app/views/routes/costs.rabl b/app/views/routes/costs.rabl new file mode 100644 index 000000000..1a1b14898 --- /dev/null +++ b/app/views/routes/costs.rabl @@ -0,0 +1,4 @@ +object @route +node :costs do +  RouteWayCostUnitConverter.convert(@route.costs) +end diff --git a/config/routes.rb b/config/routes.rb index 6313b5678..25105241a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -74,6 +74,7 @@ ChouetteIhm::Application.routes.draw do          member do            get 'edit_boarding_alighting'            put 'save_boarding_alighting' +          get 'costs'            post 'duplicate', to: 'routes#duplicate'          end          resource :journey_patterns_collection, :only => [:show, :update] diff --git a/lib/route_way_cost_unit_converter.rb b/lib/route_way_cost_unit_converter.rb new file mode 100644 index 000000000..45edbf538 --- /dev/null +++ b/lib/route_way_cost_unit_converter.rb @@ -0,0 +1,25 @@ +class RouteWayCostUnitConverter +  def self.convert(way_costs) +    return if way_costs.nil? + +    way_costs.each do |_, costs| +      costs['distance'] = self.meters_to_kilometers(costs['distance']) +      costs['time'] = self.seconds_to_minutes(costs['time']) +    end +  end + +  private + +  # Round to 2 decimal places to appease JavaScript validation +  def self.meters_to_kilometers(num) +    return 0 unless num + +    (num / 1000.0).to_i +  end + +  def self.seconds_to_minutes(num) +    return 0 unless num + +    num / 60 +  end +end diff --git a/spec/javascript/journey_patterns/components/JourneyPattern_spec.js b/spec/javascript/journey_patterns/components/JourneyPattern_spec.js index 0da75ad47..82c996424 100644 --- a/spec/javascript/journey_patterns/components/JourneyPattern_spec.js +++ b/spec/javascript/journey_patterns/components/JourneyPattern_spec.js @@ -26,7 +26,8 @@ describe('the edit button', () => {          stop_points: []        },        index: 0, -      editMode: editMode +      editMode: editMode, +      fetchRouteCosts: () => {}      }      let list = renderer.create(        <JourneyPattern @@ -38,6 +39,7 @@ describe('the edit button', () => {          onDeleteJourneyPattern={props.onDeleteJourneyPattern}          onOpenEditModal={props.onOpenEditModal}          editMode={props.editMode} +        fetchRouteCosts={props.fetchRouteCosts}        />      ) diff --git a/spec/javascript/journey_patterns/components/JourneyPatterns_spec.js b/spec/javascript/journey_patterns/components/JourneyPatterns_spec.js index 0c852deff..b6f83963b 100644 --- a/spec/javascript/journey_patterns/components/JourneyPatterns_spec.js +++ b/spec/javascript/journey_patterns/components/JourneyPatterns_spec.js @@ -15,7 +15,8 @@ describe('stopPointHeader', () => {        onLoadFirstPage: ()=>{},        onOpenEditModal: ()=>{},        stopPointsList: [stop_point, same_city_stop_point, other_country_stop_point], -      journeyPatterns: [] +      journeyPatterns: [], +      fetchRouteCosts: () => {}      }      let list = renderer.create(        <JourneyPatterns @@ -25,6 +26,7 @@ describe('stopPointHeader', () => {          onCheckboxChange={props.onCheckboxChange}          onLoadFirstPage={props.onLoadFirstPage}          onOpenEditModal={props.onOpenEditModal} +        fetchRouteCosts={props.fetchRouteCosts}        />      ).toJSON() diff --git a/spec/javascript/journey_patterns/reducers/journey_patterns_spec.js b/spec/javascript/journey_patterns/reducers/journey_patterns_spec.js index bfa87d24a..ce3bdc055 100644 --- a/spec/javascript/journey_patterns/reducers/journey_patterns_spec.js +++ b/spec/javascript/journey_patterns/reducers/journey_patterns_spec.js @@ -132,6 +132,70 @@ describe('journeyPatterns reducer', () => {      ).toEqual([state[0], new_state])    }) +  it('should handle RECEIVE_ROUTE_COSTS', () => { +    const costs = { +      "1-2": { +        distance: 1, +        time: 9, +      }, +      "2-3": { +        distance: 23, +        time: 10 +      } +    } +    const new_costs = { +      "1-2": { +        distance: 0, +        time: 10, +      }, +      "2-3": { +        distance: 23, +        time: 10 +      } +    } +    const new_state = Object.assign({}, state[1], {costs: new_costs}) +    expect( +      jpReducer(state, { +        type: 'RECEIVE_ROUTE_COSTS', +        costs, +        key: '2-3', +        index: 1 +      }) +    ).toEqual([state[0], new_state]) +  }) + +  it('should handle RECEIVE_ROUTE_COSTS when cost key is missing', () => { +    const costs = { +      "1-2": { +        distance: 1, +        time: 9, +      }, +      "2-3": { +        distance: 23, +        time: 10 +      } +    } +    const new_costs = { +      "1-2": { +        distance: 0, +        time: 10, +      }, +      "3-4": { +        distance: 0, +        time: 0 +      } +    } +    const new_state = Object.assign({}, state[1], {costs: new_costs}) +    expect( +      jpReducer(state, { +        type: 'RECEIVE_ROUTE_COSTS', +        costs, +        key: '3-4', +        index: 1 +      }) +    ).toEqual([state[0], new_state]) +  }) +    it('should handle DELETE_JOURNEYPATTERN', () => {      expect(        jpReducer(state, { diff --git a/spec/lib/route_way_cost_unit_converter_spec.rb b/spec/lib/route_way_cost_unit_converter_spec.rb new file mode 100644 index 000000000..3c5e51710 --- /dev/null +++ b/spec/lib/route_way_cost_unit_converter_spec.rb @@ -0,0 +1,38 @@ +RSpec.describe RouteWayCostUnitConverter do +  describe ".convert" do +    it "converts distance from meters to km and time from seconds to minutes " \ +      "and rounds to two decimal places" do +      costs = { +        '1-2' => { +          'distance' => 35223, +          'time' => 5604 +        }, +        '94435-97513' => { +          'distance' => 35919, +          'time' => 6174 +        }, +        '2-3' => { +          'distance' => nil, +          'time' => nil +        } +      } + +      expect( +        RouteWayCostUnitConverter.convert(costs) +      ).to eq({ +        '1-2' => { +          'distance' => 35, +          'time' => 93 +        }, +        '94435-97513' => { +          'distance' => 35, +          'time' => 102 +        }, +        '2-3' => { +          'distance' => 0, +          'time' => 0 +        } +      }) +    end +  end +end | 
