· 6 years ago · Aug 28, 2019, 09:22 AM
1import { ToolkitActionsTypes } from "mop-ui-toolkit";
2import moment from "moment";
3import * as types from "../actions/ActionTypes";
4import * as common from "./commonFunctions";
5
6function remapFrequencyGroups(frequencyGroups, calendars) {
7 const frequencies = [];
8
9 if (frequencyGroups) {
10 frequencyGroups.forEach((f, idx) => {
11 const frequency = Object.assign({}, f, { calendars: calendars.filter(c => f.calendarRefs.includes(c.id)) });
12 if (f.headwayInterval) {
13 // type headway
14 Object.assign(frequency, { key: `FRQ-${idx}`, type: "headway" });
15 // add to list
16 frequencies.push(frequency);
17 } else if (f.lastDepartureTime) {
18 // type rhythmical
19 Object.assign(frequency, { key: `FRQ-${idx}`, type: "rhythmical" });
20 // sorting timebands
21 frequency.timebands.sort((t1, t2) => (t1.startTime > t2.startTime ? 1 : -1));
22 // add to list
23 frequencies.push(frequency);
24 } else {
25 // type journeys
26 const joined = frequencies.find(e => e.type === "journeys" && e.calendarRefs.includes(f.calendarRefs[0]));
27 if (joined) {
28 // joining timebands
29 const timebands = joined.timebands.concat(f.timebands);
30 // sorting timebands
31 timebands.sort((t1, t2) => (t1.startTime > t2.startTime ? 1 : -1));
32
33 Object.assign(joined, { timebands, firstDepartureTime: timebands[0].startTime });
34 } else {
35 Object.assign(frequency, { key: `FRQ-${idx}`, type: "journeys" });
36 // sorting timebands
37 frequency.timebands.sort((t1, t2) => (t1.startTime > t2.startTime ? 1 : -1));
38 Object.assign(frequency, { firstDepartureTime: frequency.timebands[0].startTime });
39
40 // add to list
41 frequencies.push(frequency);
42 }
43 }
44 });
45 }
46
47 return frequencies;
48}
49
50function getJourneyTemplatePoints(template) {
51 const patternPointRefs = template.journeyPattern.pointsInSequence;
52 const routePoints = template.points;
53 const waitTimes = template.waitTimes;
54 const bufferTimes = template.bufferTimes;
55
56 const tPoints = [];
57 let lastRouteIdx = -1;
58
59 patternPointRefs.forEach((e, idx) => {
60 const pointRef = e.pointRef || e;
61 const point = routePoints.find((p, i) => {
62 if (i > lastRouteIdx && p.id === pointRef) {
63 lastRouteIdx = i;
64 return p;
65 }
66 return null;
67 });
68
69 if (point) {
70 const order = point.order || e.order || idx + 1;
71 const sliderIndex = point.sliderIndex || routePoints.length - lastRouteIdx;
72 const waitTime = (waitTimes && waitTimes[idx] && waitTimes[idx].waitTime) || null;
73 const bufferTime = (bufferTimes && bufferTimes[idx] && bufferTimes[idx].bufferTime) || null;
74
75 tPoints.push(Object.assign(common.remapPoint(point), { order, sliderIndex, waitTime, bufferTime }));
76 }
77 });
78
79 return tPoints;
80}
81
82// JOURNEY TEMPLATE RE-MAPPING
83export function remapJourneyTemplate(template) {
84 const journeyPattern = template.journeyPattern;
85 const tPoints = getJourneyTemplatePoints(template);
86 const tOrigin = tPoints[0];
87 const tDestination = tPoints[tPoints.length - 1];
88 // const tDistance = getJourneyTemplateDistance(journeyPattern.linksInSequence);
89
90 const calendars = template.calendars ? template.calendars.map(common.remapCalendar) : [];
91 const frequencyGroups = remapFrequencyGroups(template.frequencyGroups, calendars);
92
93 const jt = {
94 id: template.id,
95 nameLong: template.name || template.shortName,
96 nameShort: template.shortName,
97 points: tPoints,
98 origin: tOrigin && common.remapPoint(tOrigin),
99 destination: tDestination && common.remapPoint(tDestination),
100 routeRef: template.routeRef,
101 direction: journeyPattern.directionType.toUpperCase(),
102 distance: journeyPattern.distance,
103 isMonitored: template.isMonitored,
104 isService: template.isService,
105 type: template.routeType || "service",
106 calendars,
107 frequencyGroups,
108 // TODO calcolare ?
109 calendarDays: "Calendar days: 10/58",
110 generatedJourneys: template.xMetricsSummary ? template.xMetricsSummary.numberOfJourneys : 0,
111 avgSpeed: template.xMetricsSummary ? template.xMetricsSummary.averageSpeed : 0,
112 capacity: "Capacity Offered: 70"
113 };
114
115 return jt;
116}
117
118// REDUCER
119const timetableReducer = (timetable = null, action) => {
120 switch (action.type) {
121 case types.SET_TIMETABLE_SUCCESS: {
122 return Object.assign({}, action.timetable); // NEW STATE
123 }
124
125 case types.LOAD_TIMETABLE_SUCCESS: {
126 return Object.assign({}, common.remapTimetable(action.timetable)); // NEW STATE
127 }
128
129 case types.LOAD_TIMETABLES_SUCCESS: {
130 const { timetables, currentTimetableId } = action;
131
132 if (timetables && timetables.length > 0) {
133 const newTT = (currentTimetableId && timetables.find(tt => tt.id === currentTimetableId)) || timetables[0];
134
135 return Object.assign({}, common.remapTimetable(newTT)); // NEW STATE
136 }
137
138 return null; // NEW STATE
139 }
140
141 case types.DELETE_TIMETABLE_SUCCESS: {
142 if (timetable && timetable.id === action.deleted) return null; // NEW STATE
143
144 return timetable; // SAME STATE
145 }
146
147 case types.DELETE_CONNECTION_SUCCESS: {
148 // TODO const connectionIndex = timetable.connections.findIndex(s => s.id === action.deleted);
149 // TODO if (connectionIndex > -1) timetable.connections.splice(serviceIndex, 1); // removed
150
151 return Object.assign({}, timetable);
152 }
153
154 case types.LOAD_TIMETABLE_JOURNEYS: {
155 return Object.assign({}, timetable, { journeys: null }); // NEW STATE
156 }
157
158 case types.LOAD_TIMETABLE_JOURNEYS_SUCCESS: {
159 const { /* timetableRef */ journeysExtended } = action;
160
161 let timetableJourneys = [];
162 if (journeysExtended) {
163 const { calendarSummaries, lines, vehicleJourneyHeaders } = journeysExtended;
164 if (vehicleJourneyHeaders && vehicleJourneyHeaders.length > 0) {
165 timetableJourneys = vehicleJourneyHeaders.map(j => {
166 // aggiungo il calendario di questo journey
167 const calendars = [];
168 const journeyCalendarRefs = j.version.data.calendarRefs && j.version.data.calendarRefs;
169 if (journeyCalendarRefs && journeyCalendarRefs.length > 0) {
170 journeyCalendarRefs.forEach(jCalRef => {
171 const jCalendar = calendarSummaries.find(cs => cs.calendars.includes(jCalRef.id));
172 calendars.push(jCalendar);
173 });
174 }
175 // aggiungo la line di questo journey
176 const jLineRef = j.version.line;
177 const line = lines.find(l => l.id === jLineRef.id && l.versionId === jLineRef.versionId);
178 return common.remapTimetableJourney(j, calendars, line);
179 });
180 }
181 }
182
183 return Object.assign({}, timetable, { journeys: timetableJourneys }); // NEW STATE
184 }
185
186 case types.LOAD_TIMETABLE_SERVICE_SUCCESS: {
187 const service = action.service;
188 const serviceIdx = timetable.services.findIndex(elem => elem.id === service.id);
189
190 const jTemplates = service.templateVehicleJourneys.map(remapJourneyTemplate);
191
192 const detailedService = Object.assign({}, common.remapTimetableService(service), {
193 journeyTemplates: jTemplates
194 });
195
196 // TODO: una volta inserita version nelle API, mapparla correttamente
197 detailedService.version = {
198 id: moment(timetable.version.from, "YYYY-MM-DD").format("YYYYMMDD"),
199 from: timetable.version.from,
200 to: timetable.version.to,
201 status: "active"
202 }
203
204 // eslint-disable-next-line
205 timetable.services[serviceIdx] = detailedService;
206 return Object.assign({}, timetable); // NEW STATE
207 }
208
209 case types.SAVE_VEHICLE_TIMETABLE_SUCCESS: {
210 if (!action.saved) return timetable; // SAME
211
212 let jTemplates = [];
213 if (action.saved.templateVehicleJourneys)
214 jTemplates = action.saved.templateVehicleJourneys.map(remapJourneyTemplate);
215
216 const vehicleTimetable = Object.assign({}, common.remapTimetableService(action.saved), {
217 journeyTemplates: jTemplates
218 });
219
220 // TODO: una volta inserita version nelle API, mapparla correttamente
221 vehicleTimetable.version = {
222 id: moment(timetable.version.from, "YYYY-MM-DD").format("YYYYMMDD"),
223 from: timetable.version.from,
224 to: timetable.version.to,
225 status: "active"
226 }
227
228 const objIdx = timetable.services.findIndex(elem => elem.id === vehicleTimetable.id);
229 if (objIdx < 0) {
230 // no previuos vehicleTimetable found
231 timetable.services.unshift(vehicleTimetable); // add the new vehicleTimetable
232 } else {
233 // eslint-disable-next-line
234 timetable.services[objIdx] = vehicleTimetable; // update the previous vehicleTimetable
235 }
236
237 return Object.assign({}, timetable);
238 }
239
240 case types.LOAD_SERVICE_JOURNEYS_SUCCESS: {
241 const service = timetable.services.find(elem => elem.id === action.serviceId);
242 const template = service.journeyTemplates.find(elem => elem.id === action.journeyTemplateId);
243
244 // dati globali a livello di journeyTemplate
245 const journeyTemplateData = {
246 points: template.points,
247 calendars: template.calendars
248 };
249
250 let journeyList = [];
251
252 if (template.isService) journeyList = action.journeys.map(common.remapJourney, journeyTemplateData);
253 else journeyList = action.deadRuns.map(common.remapJourney, journeyTemplateData);
254
255 Object.assign(template, { journeys: journeyList }, { xMetricsSummary: action.xMetricsSummary });
256
257 return Object.assign({}, timetable); // NEW STATE
258 }
259
260 case types.LOAD_JOURNEY_SUCCESS: {
261 const service = timetable.services.find(elem => elem.id === action.serviceId);
262 const template = service.journeyTemplates.find(elem => elem.id === action.journeyTemplateId);
263
264 const journeyIdx = template.journeys.findIndex(elem => elem.id === action.journey.id);
265
266 // eslint-disable-next-line
267 template[journeyIdx] = action.journey;
268
269 return Object.assign({}, timetable); // NEW STATE
270 }
271
272 case types.LOAD_JOURNEY_CONNECTIONS_SUCCESS: {
273 const service = timetable.services.find(elem => elem.id === action.serviceId);
274 // devo fare il merge tra i punti ordinati e quelli che stanno in action.points
275 const points = action.orderedPoints;
276 const extendedPoints = points.map(point => {
277 const actionPoint = action.points.find(p => p.point.id === point.id);
278 const lines = actionPoint.lines ? actionPoint.lines.map(common.remapLine) : null;
279 const interchangeAreas =
280 actionPoint.interchangeAreas && actionPoint.interchangeAreas.length > 0
281 ? actionPoint.interchangeAreas.map(common.remapInterchangeArea)
282 : null;
283
284 return Object.assign({}, point, { type: "timetable" }, { lines }, { interchangeAreas });
285 });
286
287 // le connessioni di un punto sono le linee diverse dalla linea di questo service
288 const connectionsCounters = common.injectConnections(
289 extendedPoints,
290 service.line.id,
291 service.line.transportMode.code
292 );
293
294 const template = service.journeyTemplates.find(elem => elem.id === action.journeyTemplateId);
295
296 Object.assign(template, { points: extendedPoints }, { connectionsCounters });
297
298 return Object.assign({}, timetable); // NEW STATE
299 }
300
301 case types.LOAD_INTERCHANGE_AREA_CONNECTIONS: {
302 if (action.source !== "timetable") {
303 return timetable; // SAME STATE
304 }
305
306 const ichgAreaRef = action.ichgAreaRef;
307 const service = timetable.services.find(elem => elem.id === action.ichgAreaRef.serviceId);
308 const template = service.journeyTemplates.find(elem => elem.id === action.ichgAreaRef.journeyTemplateId);
309 const extendedPoints = template.points.map(point => {
310 const interchangeAreas =
311 point.interchangeAreas && point.interchangeAreas.length > 0
312 ? point.interchangeAreas.map(a => {
313 if (a.id === ichgAreaRef.id) {
314 return Object.assign(a, { loadingConnections: true })
315 }
316
317 return a;
318 })
319 : null;
320
321 return Object.assign({}, point, { interchangeAreas });
322 });
323
324 // le connessioni di un punto sono le linee diverse dalla linea in questione
325 const connectionsCounters = common.injectConnections(extendedPoints, service.id, service.line.transportMode.code);
326
327 Object.assign(template, { points: extendedPoints }, { connectionsCounters });
328
329 return Object.assign({}, timetable); // NEW STATE
330 }
331
332 case types.LOAD_INTERCHANGE_AREA_CONNECTIONS_SUCCESS: {
333 if (action.source !== "timetable") {
334 return timetable; // SAME STATE
335 }
336
337 const area = action.area;
338 const service = timetable.services.find(elem => elem.id === action.ichgAreaRef.serviceId);
339 const template = service.journeyTemplates.find(elem => elem.id === action.ichgAreaRef.journeyTemplateId);
340 const extendedPoints = template.points.map(point => {
341 const interchangeAreas =
342 point.interchangeAreas && point.interchangeAreas.length > 0
343 ? point.interchangeAreas.map(a => {
344 if (a.id === area.id) {
345 area.loadingConnections = false;
346 return Object.assign({}, a, common.remapInterchangeArea(area));
347 }
348
349 return a;
350 })
351 : null;
352
353 return Object.assign({}, point, { interchangeAreas });
354 });
355
356 // le connessioni di un punto sono le linee diverse dalla linea in questione
357 const connectionsCounters = common.injectConnections(extendedPoints, service.id, service.line.transportMode.code);
358
359 Object.assign(template, { points: extendedPoints }, { connectionsCounters });
360
361 return Object.assign({}, timetable); // NEW STATE
362 }
363
364 case types.LOAD_INTERCHANGE_AREA_CONNECTIONS_FAILED: {
365 if (action.source !== "timetable") {
366 return timetable; // SAME STATE
367 }
368
369 const area = action.area;
370 const service = timetable.services.find(elem => elem.id === action.ichgAreaRef.serviceId);
371 const template = service.journeyTemplates.find(elem => elem.id === action.ichgAreaRef.journeyTemplateId);
372 const extendedPoints = template.points.map(point => {
373 const interchangeAreas =
374 point.interchangeAreas && point.interchangeAreas.length > 0
375 ? point.interchangeAreas.map(a => {
376 if (a.id === area.id) {
377 return Object.assign(a, { connections: [], loadingConnections: false })
378 }
379
380 return a;
381 })
382 : null;
383
384 return Object.assign({}, point, { interchangeAreas });
385 });
386
387 Object.assign(template, { points: extendedPoints });
388
389 return Object.assign({}, timetable); // NEW STATE
390 }
391
392 case types.SAVE_JOURNEY_TEMPLATE_SUCCESS:
393 case types.UPDATE_JOURNEY_PATTERN_SUCCESS: {
394 if (!(action.saved && action.saved.id)) return timetable; // SAME
395
396 const jTemplate = remapJourneyTemplate(action.saved);
397
398 const service = timetable.services.find(elem => elem.id === action.vehicleTimetableId);
399
400 if (service.journeyTemplates) {
401 const objIdx = service.journeyTemplates.findIndex(elem => elem.id === jTemplate.id);
402 if (objIdx < 0) {
403 // no previuos journeyTemplate found
404 service.journeyTemplates.unshift(jTemplate); // add the new journeyTemplate
405 } else {
406 // eslint-disable-next-line
407 service.journeyTemplates[objIdx] = jTemplate; // update the previous journeyTemplate
408 }
409 } else {
410 Object.assign(service, { journeyTemplates: [jTemplate] });
411 }
412
413 return Object.assign({}, timetable);
414 }
415
416 case types.DELETE_VEHICLE_TIMETABLE_SUCCESS: {
417 const serviceIndex = timetable.services.findIndex(s => s.id === action.deleted);
418 if (serviceIndex > -1) timetable.services.splice(serviceIndex, 1); // removed
419
420 return Object.assign({}, timetable);
421 }
422
423 case types.DELETE_JOURNEY_TEMPLATE_SUCCESS: {
424 const serviceIndex = timetable.services.findIndex(s => s.id === action.deleted.vehicleTimetableId);
425 const journeyTemplateIndex = timetable.services[serviceIndex].journeyTemplates.findIndex(
426 jt => jt.id === action.deleted.journeyTemplateId
427 );
428
429 if (journeyTemplateIndex > -1) timetable.services[serviceIndex].journeyTemplates.splice(journeyTemplateIndex, 1); // removed
430
431 return Object.assign({}, timetable);
432 }
433
434 case types.SAVE_OPERATING_CALENDARS_SUCCESS: {
435 if (!Array.isArray(action.saved)) return timetable; // SAME
436
437 const service = timetable.services.find(elem => elem.id === action.vehicleTimetableId);
438 const jTemplate = service.journeyTemplates.find(elem => elem.id === action.journeyTemplateId);
439
440 const calendars = action.saved.map(common.remapCalendar);
441
442 Object.assign(jTemplate, { calendars });
443
444 return Object.assign({}, timetable);
445 }
446
447 case types.SAVE_FREQUENCY_GROUP_SUCCESS: {
448 if (!Array.isArray(action.saved)) return timetable; // SAME
449
450 const service = timetable.services.find(elem => elem.id === action.vehicleTimetableId);
451 const jTemplate = service.journeyTemplates.find(elem => elem.id === action.journeyTemplateId);
452
453 const frequencyGroups = remapFrequencyGroups(action.saved, jTemplate.calendars);
454
455 Object.assign(jTemplate, { frequencyGroups });
456
457 return Object.assign({}, timetable);
458 }
459
460 case types.SEARCH_CONNECTIONS: {
461 return Object.assign({}, timetable, { connections: null }); // NEW STATE
462 }
463
464 case types.SEARCH_CONNECTIONS_SUCCESS: {
465 const { connections } = action;
466 return Object.assign({}, timetable, { connections: connections.map(common.remapConnection) }); // NEW STATE
467 }
468
469 case types.SET_NETWORK:
470 case types.LOAD_TIMETABLES:
471 case types.SET_TIMETABLE:
472 case ToolkitActionsTypes.LOGOUT:
473 return null;
474
475 default:
476 return timetable; // SAME STATE
477 }
478};
479
480export default timetableReducer;