Skip to content

Commit 1a2ffd0

Browse files
committed
fix(editor): misc. editor fixes for SQL editor
1 parent b570069 commit 1a2ffd0

30 files changed

+421
-202
lines changed

gtfs.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,24 +223,24 @@
223223
required: true
224224
inputType: DROPDOWN
225225
options:
226-
- value: 'IN_PROGRESS'
226+
- value: 0
227227
text: 'In Progress'
228-
- value: 'PENDING_APPROVAL'
228+
- value: 1
229229
text: 'Pending Approval'
230-
- value: 'APPROVED'
230+
- value: 2
231231
text: 'Approved'
232232
columnWidth: 6
233233
adminOnly: true
234234
# helpContent: The route_id field contains an ID that uniquely identifies a route. The route_id is dataset unique.
235-
- name: publiclyVisible
235+
- name: publicly_visible
236236
datatools: true
237237
displayName: Public?
238238
required: true
239239
inputType: DROPDOWN
240240
options:
241-
- value: false
241+
- value: 0
242242
text: 'No'
243-
- value: true
243+
- value: 1
244244
text: 'Yes'
245245
columnWidth: 6
246246
adminOnly: true
@@ -359,7 +359,7 @@
359359
required: false
360360
inputType: TEXT
361361
columnWidth: 6
362-
helpContent: "The trip_headsign field contains the text that appears on a sign that identifies the trip's destination to passengers. Use this field to distinguish between different patterns of service in the same route. If the headsign changes during a trip, you can override the trip_headsign by specifying values for the thestop_headsign field in stop_times.txt."
362+
helpContent: "The trip_headsign field contains the text that appears on a sign that identifies the trip's destination to passengers. Use this field to distinguish between different patterns of service in the same route. If the headsign changes during a trip, you can override the trip_headsign by specifying values for the the stop_headsign field in stop_times.txt."
363363
- name: "trip_short_name"
364364
required: false
365365
inputType: TEXT

lib/common/util/config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export function getConfigProperty (propertyString: string): ?any {
88
return objectPath.get(window.DT_CONFIG, propertyString)
99
}
1010

11+
export function getGtfsSpec (): Array<any> {
12+
return window.DT_CONFIG.modules.editor.spec
13+
}
14+
1115
export function getGtfsPlusSpec (): Array<GtfsPlusTable> {
1216
return window.DT_CONFIG.modules.gtfsplus.spec
1317
}

lib/editor/actions/active.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export function saveEntity (feedId, entity, component) {
201201
.then(savedEntity => {
202202
const namespace = getEditorNamespace(feedId, getState())
203203
// Refetch entity and replace in store
204-
dispatch(fetchGTFSEntities({
204+
return dispatch(fetchGTFSEntities({
205205
namespace,
206206
id: savedEntity.id,
207207
type: component,

lib/editor/actions/map/stopStrategies.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import along from '@turf/along'
22
import ll from '@conveyal/lonlat'
3+
import clone from 'lodash.clonedeep'
34
import lineDistance from 'turf-line-distance'
45
import lineSlice from 'turf-line-slice'
6+
import lineString from 'turf-lineString'
7+
import point from 'turf-point'
58

69
import {updateActiveGtfsEntity, saveActiveGtfsEntity} from '../active'
10+
import {generateUID} from '../../../common/util/util'
11+
import {POINT_TYPE} from '../../constants'
712
import {newGtfsEntity} from '../editor'
813
import {setErrorMessage} from '../../../manager/actions/status'
914
import {getTableById} from '../../util/gtfs'
@@ -180,7 +185,7 @@ export function addStopToPattern (pattern, stop, index) {
180185
dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {patternStops}))
181186
// saveActiveGtfsEntity('trippattern')
182187
const {stop_lon: lng, stop_lat: lat} = stop
183-
return dispatch(extendPatternToPoint(pattern, endPoint, {lng, lat}))
188+
return dispatch(extendPatternToPoint(pattern, endPoint, {lng, lat}, stop))
184189
} else {
185190
// If shape coordinates do not exist, add pattern stop and get shape
186191
// between stops (if multiple stops exist).
@@ -220,24 +225,36 @@ export function addStopToPattern (pattern, stop, index) {
220225
* Extends shape of input pattern from specified end point to new end point,
221226
* optionally following streets if the setting is enabled.
222227
*/
223-
function extendPatternToPoint (pattern, endPoint, newEndPoint) {
228+
function extendPatternToPoint (pattern, endPoint, newEndPoint, stop = null) {
224229
return async function (dispatch, getState) {
225230
const {followStreets} = getState().editor.editSettings.present
226231
const {controlPoints, patternSegments} = getControlPoints(getState())
232+
const clonedControlPoints = clone(controlPoints)
227233
let newShape
228234
if (followStreets) {
229235
newShape = await getPolyline([endPoint, newEndPoint])
230236
}
231237
// get single coordinate for straight line if polyline fails or if not following streets
232238
if (!newShape) {
233-
newShape = [ll.toCoordinates(newEndPoint)]
239+
newShape = [ll.toCoordinates(endPoint), ll.toCoordinates(newEndPoint)]
234240
}
235241
// append newShape coords to existing pattern coords
236242
const shape = {type: 'LineString', coordinates: [...pattern.shape.coordinates, ...newShape]}
237-
// Update pattern geometry
243+
// If extending to a stop, add control point for stop
244+
if (stop) {
245+
const controlPoint = {
246+
id: generateUID(),
247+
point: point(ll.toCoordinates(newEndPoint)),
248+
pointType: POINT_TYPE.STOP,
249+
distance: lineDistance(lineString(newShape), 'meters'),
250+
stopId: stop.stop_id
251+
}
252+
clonedControlPoints.push(controlPoint)
253+
}
254+
// Update pattern geometry and control points
238255
dispatch(updatePatternGeometry({
239256
// FIXME Should control points be updated here?
240-
controlPoints,
257+
controlPoints: clonedControlPoints,
241258
patternSegments: [...patternSegments, newShape]
242259
}))
243260
await dispatch(saveActiveGtfsEntity('trippattern'))

lib/editor/actions/trip.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,51 +27,57 @@ export const removeTrips = createAction('REMOVE_TRIPS')
2727
export function fetchTripsForCalendar (feedId, pattern, calendarId) {
2828
return function (dispatch, getState) {
2929
const namespace = getEditorNamespace(feedId, getState())
30-
// FIXME: pattern integer id hack (shouldn't need quotes around pattern_id var)
30+
// This fetches patterns on the pattern_id field (rather than ID) because
31+
// pattern_id is needed to join on the nested trips table
3132
const query = `query ($namespace: String, $pattern_id: [String], $service_id: [String]) {
3233
feed(namespace: $namespace) {
3334
patterns (pattern_id: $pattern_id) {
3435
id: pattern_id
35-
trips (service_id: $service_id) {
36+
trips (service_id: $service_id, limit: -1) {
3637
id
3738
tripId: trip_id
3839
tripHeadsign: trip_headsign
3940
tripShortName: trip_short_name
4041
blockId: block_id
4142
directionId: direction_id
4243
route_id
44+
shape_id
45+
wheelchair_accessible
46+
bikes_allowed
47+
pattern_id
4348
service_id
44-
stopTimes: stop_times {
49+
stopTimes: stop_times (limit: -1) {
4550
stopId: stop_id
4651
stopSequence: stop_sequence
4752
arrivalTime: arrival_time
4853
departureTime: departure_time
4954
stopHeadsign: stop_headsign
5055
shape_dist_traveled: shape_dist_traveled
56+
pickup_type
57+
drop_off_type
58+
timepoint
5159
}
5260
}
5361
}
5462
}
5563
}`
5664
dispatch(requestingTripsForCalendar({feedId, pattern, calendarId, query}))
5765
// FIXME: string casting pattern id
58-
return dispatch(fetchGraphQL({query, variables: {namespace, pattern_id: `${pattern.id}`, service_id: calendarId}}))
66+
return dispatch(fetchGraphQL({query, variables: {namespace, pattern_id: pattern.patternId, service_id: calendarId}}))
5967
.then(res => res.json())
6068
.then(data => dispatch(receiveTripsForCalendar({trips: data.feed.patterns[0].trips, pattern})))
69+
.catch(err => {
70+
console.log(err)
71+
dispatch(setErrorMessage({message: 'Could not fetch trips for pattern'}))
72+
})
6173
}
6274
}
6375

6476
export function saveTripsForCalendar (feedId, pattern, calendarId, trips) {
6577
return function (dispatch, getState) {
6678
let errorCount = 0
6779
const errorIndexes = []
68-
// FIXME: add rest of mapping
69-
trips = trips
70-
.map(t => ({
71-
serviceId: t.calendarId,
72-
...t
73-
}))
74-
.map(snakeCaseKeys)
80+
trips = trips.map(snakeCaseKeys)
7581
dispatch(savingTrips({feedId, pattern, calendarId, trips}))
7682
return Promise.all(trips.filter(t => t).map((trip, index) => {
7783
const tripExists = !entityIsNew(trip) && trip.id !== null

lib/editor/actions/tripPattern.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import {createAction} from 'redux-actions'
22
import snakeCaseKeys from 'snakecase-keys'
33

44
import {secureFetch} from '../../common/actions'
5-
import {setActiveGtfsEntity, updateEditSetting} from './active'
6-
import {entityIsNew, tripPatternToGtfs} from '../util/objects'
5+
import {fetchGTFSEntities} from '../../manager/actions/versions'
6+
import {updateEditSetting} from './active'
7+
import {entityIsNew} from '../util/objects'
8+
import {getEditorNamespace} from '../util/gtfs'
79

810
// TRIP PATTERNS
911

@@ -13,7 +15,6 @@ export const undoActiveTripPatternEdits = createAction('UNDO_TRIP_PATTERN_EDITS'
1315
export const setActiveStop = createAction('SET_ACTIVE_PATTERN_STOP')
1416
export const togglingPatternEditing = createAction('TOGGLE_PATTERN_EDITING')
1517
export const setActivePatternSegment = createAction('SET_ACTIVE_PATTERN_SEGMENT')
16-
const savedTripPattern = createAction('SAVED_TRIP_PATTERN')
1718
export const resnapStops = createAction('RESNAP_STOPS')
1819

1920
export function togglePatternEditing () {
@@ -44,7 +45,9 @@ export function saveTripPattern (feedId, tripPattern) {
4445
return function (dispatch, getState) {
4546
const patternIsNew = entityIsNew(tripPattern)
4647
const method = patternIsNew ? 'post' : 'put'
47-
const routeId = tripPattern.routeId
48+
// Route ID needed for re-fetch is the ID field of the active entity (route)
49+
// NOTE: The pattern being saved is the active **sub**-entity.
50+
const routeId = getState().editor.data.active.entity.id
4851
const data = snakeCaseKeys(tripPattern)
4952
// Convert control points and pattern segments into shape points
5053
// FIXME: ready shape_points for insertion into shapes table
@@ -56,14 +59,18 @@ export function saveTripPattern (feedId, tripPattern) {
5659
data.id = patternIsNew ? null : tripPattern.id
5760
return dispatch(secureFetch(url, method, data))
5861
.then(res => res.json())
59-
.then(tripPattern => {
60-
dispatch(savedTripPattern({feedId, tripPattern: tripPatternToGtfs(tripPattern)}))
61-
if (patternIsNew) {
62-
dispatch(setActiveGtfsEntity(feedId, 'route', routeId, 'trippattern', tripPattern.id))
63-
}
64-
// dispatch(fetchTripPatternsForRoute(feedId, routeId))
65-
// const tp = tripPattern
66-
return tripPattern
62+
.then(newTripPattern => {
63+
const namespace = getEditorNamespace(feedId, getState())
64+
// // Refetch entity and replace in store
65+
console.log(`setting active pattern to ${newTripPattern.id}`)
66+
return dispatch(fetchGTFSEntities({
67+
namespace,
68+
id: routeId,
69+
type: 'route',
70+
editor: true,
71+
replaceNew: patternIsNew,
72+
patternId: newTripPattern.id
73+
}))
6774
})
6875
}
6976
}

lib/editor/components/EditorFeedSourcePanel.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import Icon from '@conveyal/woonerf/components/icon'
22
import React, {Component, PropTypes} from 'react'
3-
import { Panel, Row, Col, ButtonGroup, Button, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap'
4-
import { LinkContainer } from 'react-router-bootstrap'
3+
import {Panel, Row, Col, ButtonGroup, Button, Glyphicon, ListGroup, ListGroupItem} from 'react-bootstrap'
4+
import {LinkContainer} from 'react-router-bootstrap'
55
import moment from 'moment'
66

77
import CreateSnapshotModal from '../../editor/components/CreateSnapshotModal'
88
import ConfirmModal from '../../common/components/ConfirmModal'
9-
import { getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config'
9+
import {getComponentMessages, getMessage, getConfigProperty} from '../../common/util/config'
10+
import {isEditingDisabled} from '../../manager/util'
1011

1112
export default class EditorFeedSourcePanel extends Component {
1213
static propTypes = {
@@ -43,20 +44,27 @@ export default class EditorFeedSourcePanel extends Component {
4344
})
4445
}
4546

47+
isSnapshotActive = snapshot => {
48+
return snapshot.feedLoadResult &&
49+
snapshot.feedLoadResult.uniqueIdentifier === this.props.feedSource.editorNamespace
50+
}
51+
4652
render () {
4753
const {
4854
feedSource,
4955
project,
5056
user
5157
} = this.props
5258
const disabled = !user.permissions.hasFeedPermission(project.organizationId, project.id, feedSource.id, 'manage-feed')
53-
const editDisabled = !user.permissions.hasFeedPermission(project.organizationId, project.id, feedSource.id, 'edit-gtfs')
59+
const editDisabled = isEditingDisabled(user, feedSource, project)
5460
const hasVersions = feedSource && feedSource.feedVersions && feedSource.feedVersions.length > 0
5561
const currentSnapshot = feedSource.editorSnapshots && feedSource.editorSnapshots.length
56-
? feedSource.editorSnapshots.find(s => s.current)
62+
? feedSource.editorSnapshots.find(this.isSnapshotActive)
5763
: null
5864
const inactiveSnapshots = feedSource.editorSnapshots
59-
? feedSource.editorSnapshots.filter(s => !s.current).sort((a, b) => b.snapshotTime - a.snapshotTime)
65+
? feedSource.editorSnapshots
66+
.filter(s => !this.isSnapshotActive(s))
67+
.sort((a, b) => b.snapshotTime - a.snapshotTime)
6068
: []
6169
return (
6270
<Row>
@@ -67,11 +75,13 @@ export default class EditorFeedSourcePanel extends Component {
6775
<Col xs={9}>
6876
{feedSource.editorSnapshots && feedSource.editorSnapshots.length
6977
? <div>
78+
{/* This is the active snapshot */}
7079
<Panel bsStyle='success' header={<h3>Active snapshot</h3>}>
7180
{currentSnapshot
7281
? <ListGroup fill>
7382
<SnapshotItem
7483
modal={this.refs.confirmModal}
84+
isActive
7585
disabled={disabled}
7686
snapshot={currentSnapshot}
7787
{...this.props} />
@@ -81,6 +91,7 @@ export default class EditorFeedSourcePanel extends Component {
8191
</ListGroup>
8292
}
8393
</Panel>
94+
{/* These are the inactive snapshots */}
8495
<Panel bsStyle='warning' header={<h3>Inactive snapshots</h3>}>
8596
<ListGroup fill>
8697
{inactiveSnapshots.length === 0
@@ -90,6 +101,7 @@ export default class EditorFeedSourcePanel extends Component {
90101
<SnapshotItem
91102
modal={this.refs.confirmModal}
92103
key={s.id}
104+
isActive={false}
93105
disabled={disabled}
94106
snapshot={s}
95107
{...this.props} />
@@ -145,6 +157,7 @@ export default class EditorFeedSourcePanel extends Component {
145157

146158
class SnapshotItem extends Component {
147159
static propTypes = {
160+
isActive: PropTypes.bool,
148161
modal: PropTypes.object.isRequired,
149162
snapshot: PropTypes.object.isRequired,
150163
feedSource: PropTypes.object.isRequired
@@ -175,7 +188,7 @@ class SnapshotItem extends Component {
175188
}
176189

177190
render () {
178-
const {disabled, snapshot} = this.props
191+
const {disabled, isActive, snapshot} = this.props
179192
const dateFormat = getConfigProperty('application.date_format')
180193
const timeFormat = 'h:MMa'
181194
return (
@@ -190,9 +203,9 @@ class SnapshotItem extends Component {
190203
<ButtonGroup className='pull-right' style={{marginTop: '-20px'}}>
191204
<Button
192205
bsSize='small'
193-
disabled={snapshot.current || disabled}
206+
disabled={isActive || disabled}
194207
onClick={this._onRestoreSnapshot}>
195-
{snapshot.current
208+
{isActive
196209
? <span><Icon type='check-circle' /> {getMessage(this.messages, 'active')}</span>
197210
: <span><Glyphicon glyph='pencil' /> {getMessage(this.messages, 'restore')}</span>
198211
}

lib/editor/components/FareRulesForm.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, {Component, PropTypes} from 'react'
33
import { Checkbox, Button, FormGroup, Panel } from 'react-bootstrap'
44

55
import FareRuleSelections from './FareRuleSelections'
6-
import {getTableById} from '../util/gtfs'
6+
import {generateNullProps, getTableById} from '../util/gtfs'
77

88
const FARE_RULE_TYPES = [
99
{type: 'route_id', label: 'Route'},
@@ -24,7 +24,11 @@ export default class FareRulesForm extends Component {
2424
_onClickAdd = () => {
2525
const {activeComponent, activeEntity, updateActiveEntity} = this.props
2626
const rules = [...activeEntity.fare_rules]
27-
rules.unshift({fare_id: activeEntity.fare_id})
27+
// Add new fare rule to beginning of array
28+
rules.unshift({
29+
...generateNullProps('fare_rules'),
30+
fare_id: activeEntity.fare_id
31+
})
2832
updateActiveEntity(activeEntity, activeComponent, {fare_rules: rules})
2933
}
3034

0 commit comments

Comments
 (0)