UNPKG

@golemio/pid

Version:
260 lines 12.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JISInfotextStopSuppressionFilter = void 0; const JISInfotextSeverityLevelPriority_1 = require("../../../helpers/jis/JISInfotextSeverityLevelPriority"); const tsyringe_1 = require("@golemio/core/dist/shared/tsyringe"); let JISInfotextStopSuppressionFilter = class JISInfotextStopSuppressionFilter { filterInfotexts(infotexts, { includeFuture }) { if (!includeFuture) { return this.filterInfotextsExcludingFuture(infotexts); } else { return this.filterInfotextsIncludingFuture(infotexts); } } filterInfotextsExcludingFuture(infotexts) { const topInfotextSeverityLevelByStopId = this.mapTopInfotextSeverityLevelByStopId(infotexts); const filteredInfotexts = []; for (const infotext of infotexts) { if (!infotext.stops) { continue; } const infotextPrio = JISInfotextSeverityLevelPriority_1.JISInfotextSeverityLevelPriority[infotext.severity_level]; // Assign directly to the original object — not a spread copy. For Sequelize model // instances, `stops` is a prototype getter (not an own property), so a spread copy // would not include it, causing a TypeError on the filter call. infotext.stops = infotext.stops.filter((stop) => topInfotextSeverityLevelByStopId.get(this.getStopId(stop)) === infotextPrio); if (infotext.stops.length > 0) { filteredInfotexts.push(infotext); } } return filteredInfotexts; } getStopId(stop) { if ("getDataValue" in stop && typeof stop.getDataValue === "function") { return stop.getDataValue("stop_id"); } else { return stop.stop_id; } } mapTopInfotextSeverityLevelByStopId(infotexts) { const topInfotextSeverityLevelByStopId = new Map(); for (const infotext of infotexts) { const infotextPrio = JISInfotextSeverityLevelPriority_1.JISInfotextSeverityLevelPriority[infotext.severity_level]; for (const stop of infotext.stops ?? []) { const currentValue = topInfotextSeverityLevelByStopId.get(this.getStopId(stop)) ?? -Infinity; if (infotextPrio > currentValue) { topInfotextSeverityLevelByStopId.set(this.getStopId(stop), infotextPrio); } } } return topInfotextSeverityLevelByStopId; } filterInfotextsIncludingFuture(infotexts) { const infotextIntermediaries = this.createInfotextIntermediaries(infotexts); const infotextsByStopId = this.mapInfotextsByStopId(infotextIntermediaries); const filteredInfotextsByStopId = new Map(); for (const [stopId, infotextsForStop] of infotextsByStopId.entries()) { const filteredInfotextsForStop = this.filterStopInfotexts(infotextsForStop); filteredInfotextsByStopId.set(stopId, filteredInfotextsForStop); } const filteredInfotextsIdsByStopId = this.mapInfotextIdsByStopId(filteredInfotextsByStopId); const filteredInfotexts = this.filterAllInfotexts(infotextIntermediaries, filteredInfotextsIdsByStopId); return this.createInfotextsFromIntermediaries(filteredInfotexts); } createInfotextIntermediaries(infotexts) { return infotexts.map((infotext) => ({ data: infotext, active_period_start: new Date(infotext.active_period_start), active_period_end: infotext.active_period_end ? new Date(infotext.active_period_end) : null, })); } mapInfotextsByStopId(infotexts) { const infotextsByStopId = new Map(); for (const infotext of infotexts) { for (const stop of infotext.data.stops ?? []) { const stopId = this.getStopId(stop); if (!infotextsByStopId.has(stopId)) { infotextsByStopId.set(stopId, []); } const infotextsForStop = infotextsByStopId.get(stopId); infotextsForStop.push(infotext); } } return infotextsByStopId; } filterStopInfotexts(infotexts) { const segments = this.segmentInfotexts(infotexts); const filteredSegments = this.sortAndFilterSegments(segments); return this.findUniqueInfotextsFromSegments(filteredSegments); } segmentInfotexts(infotexts) { const maxSegmentationDate = this.findSegmentationThreshold(infotexts); const segments = []; for (const infotext of infotexts) { if (infotext.data.repeat_enabled) { segments.push(...this.segmentRepeatedInfotext(infotext, maxSegmentationDate)); } else { const start = infotext.active_period_start; const end = infotext.active_period_end; segments.push({ infotext, start, end }); } } return segments; } findSegmentationThreshold(infotexts) { let maxEndDate = new Date(0); let maxStartDate = new Date(0); for (const infotext of infotexts) { if (infotext.active_period_end && infotext.active_period_end > maxEndDate) { maxEndDate = infotext.active_period_end; } if (infotext.active_period_start > maxStartDate) { maxStartDate = infotext.active_period_start; } } return this.addDay(maxEndDate > maxStartDate ? maxEndDate : maxStartDate); } addDay(date, multiplier = 1) { const newDate = new Date(date); newDate.setDate(date.getDate() + multiplier); return newDate; } *segmentRepeatedInfotext(infotext, segmentationThreshold) { // Repeat time windows going through or ending at midnight (e.g. 23:00-01:00, 23:00-00:00) const isInfotextActiveUntilOrOverMidnight = infotext.data.repeat_time_end < infotext.data.repeat_time_start; const minSegmentationDay = isInfotextActiveUntilOrOverMidnight ? this.addDay(infotext.active_period_start, -1) : infotext.active_period_start; const maxSegmentationDay = this.getMaxSegmentationDayForInfotext(infotext, segmentationThreshold); for (let day = minSegmentationDay; day <= maxSegmentationDay; day = this.addDay(day)) { const start = this.getDatetimeFromRepeatTime(infotext.data.repeat_time_start, day); let end = this.getDatetimeFromRepeatTime(infotext.data.repeat_time_end, day); if (isInfotextActiveUntilOrOverMidnight) { end = this.addDay(end); } const boundedSegment = this.fitFiniteSegmentToInfotextTimeBounds({ infotext, start, end }); if (!boundedSegment) { continue; } yield boundedSegment; } if (!infotext.active_period_end) { const start = this.getDatetimeFromRepeatTime(infotext.data.repeat_time_start, this.addDay(maxSegmentationDay)); yield { infotext, start, end: null }; } } getMaxSegmentationDayForInfotext(infotext, segmentationThreshold) { return infotext.active_period_end && infotext.active_period_end <= segmentationThreshold ? infotext.active_period_end : segmentationThreshold; } getDatetimeFromRepeatTime(repeatTime, day) { const [hours, minutes, seconds] = repeatTime.split(":").map((fragment) => parseInt(fragment, 10)); const datetime = new Date(day); datetime.setHours(hours, minutes, seconds ?? 0, 0); return datetime; } fitFiniteSegmentToInfotextTimeBounds(segment) { let { infotext, start, end } = segment; if (start < infotext.active_period_start) { start = infotext.active_period_start; } if (infotext.active_period_end && end > infotext.active_period_end) { end = infotext.active_period_end; } if (start > end) { return null; } return { infotext, start, end }; } sortAndFilterSegments(segments) { segments.sort((a, b) => { const startTimeComparisonResult = a.start.getTime() - b.start.getTime(); if (startTimeComparisonResult !== 0) { return startTimeComparisonResult; } else if (this.isSegmentBLesserThanA(a, b)) { return -1; } else if (this.isSegmentBLesserThanA(b, a)) { return 1; } else { return 0; } }); const filteredSegments = []; let prevSegment = segments.at(0); for (let i = 1; i < segments.length; i++) { const currentSegment = segments[i]; if (this.isSegmentBLesserThanA(prevSegment, currentSegment)) { continue; } filteredSegments.push(prevSegment); prevSegment = currentSegment; } if (prevSegment) { filteredSegments.push(prevSegment); } return filteredSegments; } isSegmentBLesserThanA(a, b) { const aIsMoreSevere = JISInfotextSeverityLevelPriority_1.JISInfotextSeverityLevelPriority[b.infotext.data.severity_level] < JISInfotextSeverityLevelPriority_1.JISInfotextSeverityLevelPriority[a.infotext.data.severity_level]; const aDoesNotStartLater = b.start >= a.start; const aDoesNotEndSooner = !a.end || (b.end && b.end <= a.end) || false; const bIsWithinTimeBoundsOfA = aDoesNotStartLater && aDoesNotEndSooner; return aIsMoreSevere && bIsWithinTimeBoundsOfA; } findUniqueInfotextsFromSegments(segments) { const infotexts = new Map(); for (const segment of segments) { infotexts.set(segment.infotext.data.id, segment.infotext); } return [...infotexts.values()]; } mapInfotextIdsByStopId(infotextsByStopId) { const infotextsIdsByStopId = new Map(); for (const [stopId, infotextsForStop] of infotextsByStopId.entries()) { const infotextIdsForStop = infotextsForStop.map((infotext) => infotext.data.id); infotextsIdsByStopId.set(stopId, new Set(infotextIdsForStop)); } return infotextsIdsByStopId; } filterAllInfotexts(infotexts, filteredInfotextsIdsByStopId) { const filteredInfotexts = []; for (const infotext of infotexts) { if (!infotext.data.stops) { continue; } const filteredStops = infotext.data.stops.filter((stop) => filteredInfotextsIdsByStopId.get(this.getStopId(stop))?.has(infotext.data.id) ?? false); if (filteredStops.length > 0) { // Same as filterInfotextsExcludingFuture: mutate in-place rather than spreading, // because spreading a Sequelize model loses its prototype-getter-backed properties. // `infotext.data` is a concrete union type; direct assignment hits TypeScript's // union-write restriction (requires intersection of member types). Object.assign // sidesteps that while still invoking the Sequelize setter at runtime. Object.assign(infotext.data, { stops: filteredStops }); filteredInfotexts.push(infotext); } } return filteredInfotexts; } createInfotextsFromIntermediaries(infotextIntermediaries) { return infotextIntermediaries.map((intermediary) => intermediary.data); } }; exports.JISInfotextStopSuppressionFilter = JISInfotextStopSuppressionFilter; exports.JISInfotextStopSuppressionFilter = JISInfotextStopSuppressionFilter = __decorate([ (0, tsyringe_1.injectable)() ], JISInfotextStopSuppressionFilter); //# sourceMappingURL=JISInfotextStopSuppressionFilter.js.map