aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/routes_controller.rb4
-rw-r--r--app/javascript/journey_patterns/actions/index.js30
-rw-r--r--app/javascript/journey_patterns/components/JourneyPattern.js26
-rw-r--r--app/javascript/journey_patterns/components/JourneyPatterns.js4
-rw-r--r--app/javascript/journey_patterns/containers/JourneyPatternList.js3
-rw-r--r--app/javascript/journey_patterns/reducers/journeyPatterns.js11
-rw-r--r--app/views/journey_patterns_collections/show.html.slim1
-rw-r--r--app/views/routes/costs.rabl4
-rw-r--r--config/routes.rb1
-rw-r--r--lib/route_way_cost_unit_converter.rb25
-rw-r--r--spec/javascript/journey_patterns/components/JourneyPattern_spec.js4
-rw-r--r--spec/javascript/journey_patterns/components/JourneyPatterns_spec.js4
-rw-r--r--spec/javascript/journey_patterns/reducers/journey_patterns_spec.js64
-rw-r--r--spec/lib/route_way_cost_unit_converter_spec.rb38
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