@golemio/pid
Version:
Golemio PID Module
260 lines • 12.6 kB
JavaScript
"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