@golemio/pid 6.4.0 → 6.4.1-dev.3054859807
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/example/00_clear_test_data.sql +1 -0
- package/db/example/24_jis_delayed_segments.sql +142 -0
- package/db/migrations/postgresql/20261027100000-jis-delayed-segments.js +16 -0
- package/db/migrations/postgresql/sqls/20250127134438-departure-additional-indexes-down.sql +20 -2
- package/db/migrations/postgresql/sqls/20261027100000-jis-delayed-segments-down.sql +17 -0
- package/db/migrations/postgresql/sqls/20261027100000-jis-delayed-segments-up.sql +42 -0
- package/dist/integration-engine/jis/datasources/JISDelayedSegmentsDataSourceFactory.d.ts +9 -0
- package/dist/integration-engine/jis/datasources/JISDelayedSegmentsDataSourceFactory.js +54 -0
- package/dist/integration-engine/jis/datasources/JISDelayedSegmentsDataSourceFactory.js.map +1 -0
- package/dist/integration-engine/jis/ioc/Di.js +10 -0
- package/dist/integration-engine/jis/ioc/Di.js.map +1 -1
- package/dist/integration-engine/jis/ioc/JISContainerToken.d.ts +5 -0
- package/dist/integration-engine/jis/ioc/JISContainerToken.js +5 -0
- package/dist/integration-engine/jis/ioc/JISContainerToken.js.map +1 -1
- package/dist/integration-engine/jis/repositories/JISDelayedSegmentEnrichmentRepository.d.ts +14 -0
- package/dist/integration-engine/jis/repositories/JISDelayedSegmentEnrichmentRepository.js +69 -0
- package/dist/integration-engine/jis/repositories/JISDelayedSegmentEnrichmentRepository.js.map +1 -0
- package/dist/integration-engine/jis/repositories/JISDelayedSegmentsRepository.d.ts +23 -0
- package/dist/integration-engine/jis/repositories/JISDelayedSegmentsRepository.js +73 -0
- package/dist/integration-engine/jis/repositories/JISDelayedSegmentsRepository.js.map +1 -0
- package/dist/integration-engine/jis/repositories/helpers/JISDelayedSegmentEnrichmentQuery.d.ts +13 -0
- package/dist/integration-engine/jis/repositories/helpers/JISDelayedSegmentEnrichmentQuery.js +266 -0
- package/dist/integration-engine/jis/repositories/helpers/JISDelayedSegmentEnrichmentQuery.js.map +1 -0
- package/dist/integration-engine/jis/repositories/interfaces/IJISDelayedSegmentEnrichmentParams.d.ts +23 -0
- package/dist/integration-engine/jis/repositories/interfaces/IJISDelayedSegmentEnrichmentParams.js +2 -0
- package/dist/integration-engine/jis/repositories/interfaces/IJISDelayedSegmentEnrichmentParams.js.map +1 -0
- package/dist/integration-engine/jis/services/JISDelayedSegmentsDataService.d.ts +38 -0
- package/dist/integration-engine/jis/services/JISDelayedSegmentsDataService.js +174 -0
- package/dist/integration-engine/jis/services/JISDelayedSegmentsDataService.js.map +1 -0
- package/dist/integration-engine/jis/workers/JISWorker.js +1 -0
- package/dist/integration-engine/jis/workers/JISWorker.js.map +1 -1
- package/dist/integration-engine/jis/workers/tasks/RefreshDelayedSegmentsTask.d.ts +15 -0
- package/dist/integration-engine/jis/workers/tasks/RefreshDelayedSegmentsTask.js +55 -0
- package/dist/integration-engine/jis/workers/tasks/RefreshDelayedSegmentsTask.js.map +1 -0
- package/dist/integration-engine/vehicle-positions/workers/runs/helpers/TramMessageFilter.d.ts +2 -0
- package/dist/integration-engine/vehicle-positions/workers/runs/helpers/TramMessageFilter.js +16 -0
- package/dist/integration-engine/vehicle-positions/workers/runs/helpers/TramMessageFilter.js.map +1 -1
- package/dist/output-gateway/jis/controllers/v1/V1DelayedSegmentsController.d.ts +9 -0
- package/dist/output-gateway/jis/controllers/v1/V1DelayedSegmentsController.js +54 -0
- package/dist/output-gateway/jis/controllers/v1/V1DelayedSegmentsController.js.map +1 -0
- package/dist/output-gateway/jis/data-access/JISDelayedSegmentsRepository.d.ts +14 -0
- package/dist/output-gateway/jis/data-access/JISDelayedSegmentsRepository.js +56 -0
- package/dist/output-gateway/jis/data-access/JISDelayedSegmentsRepository.js.map +1 -0
- package/dist/output-gateway/jis/domain/IJISDelayedSegmentDto.d.ts +12 -0
- package/dist/output-gateway/jis/domain/IJISDelayedSegmentDto.js +2 -0
- package/dist/output-gateway/jis/domain/IJISDelayedSegmentDto.js.map +1 -0
- package/dist/output-gateway/jis/dto/interfaces/IDelayedSegmentsQueryDto.d.ts +5 -0
- package/dist/output-gateway/jis/dto/interfaces/IDelayedSegmentsQueryDto.js +2 -0
- package/dist/output-gateway/jis/dto/interfaces/IDelayedSegmentsQueryDto.js.map +1 -0
- package/dist/output-gateway/jis/helpers/JISDelayedSegmentInfoPanelStopsFilter.d.ts +8 -0
- package/dist/output-gateway/jis/helpers/JISDelayedSegmentInfoPanelStopsFilter.js +13 -0
- package/dist/output-gateway/jis/helpers/JISDelayedSegmentInfoPanelStopsFilter.js.map +1 -0
- package/dist/output-gateway/jis/ioc/Di.js +6 -0
- package/dist/output-gateway/jis/ioc/Di.js.map +1 -1
- package/dist/output-gateway/jis/ioc/OgJisToken.d.ts +3 -0
- package/dist/output-gateway/jis/ioc/OgJisToken.js +3 -0
- package/dist/output-gateway/jis/ioc/OgJisToken.js.map +1 -1
- package/dist/output-gateway/jis/routers/v1/V1JISRouter.d.ts +2 -0
- package/dist/output-gateway/jis/routers/v1/V1JISRouter.js +9 -0
- package/dist/output-gateway/jis/routers/v1/V1JISRouter.js.map +1 -1
- package/dist/output-gateway/jis/transformations/JISDelayedSegmentsTransformation.d.ts +7 -0
- package/dist/output-gateway/jis/transformations/JISDelayedSegmentsTransformation.js +32 -0
- package/dist/output-gateway/jis/transformations/JISDelayedSegmentsTransformation.js.map +1 -0
- package/dist/schema-definitions/jis/datasources/MapaPidDelayShapesJsonSchema.d.ts +3 -0
- package/dist/schema-definitions/jis/datasources/MapaPidDelayShapesJsonSchema.js +16 -0
- package/dist/schema-definitions/jis/datasources/MapaPidDelayShapesJsonSchema.js.map +1 -0
- package/dist/schema-definitions/jis/datasources/interfaces/IMapaPidDelayShape.d.ts +10 -0
- package/dist/schema-definitions/jis/datasources/interfaces/IMapaPidDelayShape.js +2 -0
- package/dist/schema-definitions/jis/datasources/interfaces/IMapaPidDelayShape.js.map +1 -0
- package/dist/schema-definitions/jis/datasources/interfaces/index.d.ts +1 -0
- package/dist/schema-definitions/jis/datasources/interfaces/index.js +1 -0
- package/dist/schema-definitions/jis/datasources/interfaces/index.js.map +1 -1
- package/dist/schema-definitions/jis/models/JISDelayedSegmentsModel.d.ts +24 -0
- package/dist/schema-definitions/jis/models/JISDelayedSegmentsModel.js +138 -0
- package/dist/schema-definitions/jis/models/JISDelayedSegmentsModel.js.map +1 -0
- package/dist/schema-definitions/jis/models/interfaces/IJISDelayedSegment.d.ts +44 -0
- package/dist/schema-definitions/jis/models/interfaces/IJISDelayedSegment.js +2 -0
- package/dist/schema-definitions/jis/models/interfaces/IJISDelayedSegment.js.map +1 -0
- package/dist/schema-definitions/jis/models/interfaces/index.d.ts +1 -0
- package/dist/schema-definitions/jis/models/interfaces/index.js +1 -0
- package/dist/schema-definitions/jis/models/interfaces/index.js.map +1 -1
- package/docs/assets/pid_jis_erd.png +0 -0
- package/docs/asyncapi.yaml +15 -0
- package/docs/implementation_documentation.md +56 -0
- package/docs/index.md +1 -0
- package/docs/jis/delayed-segments.md +241 -0
- package/docs/jis/index.md +9 -0
- package/docs/openapi-output.yaml +176 -0
- package/package.json +2 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { PG_SCHEMA } from "#sch/const";
|
|
14
|
+
import { JISDelayedSegmentsModel } from "#sch/jis/models/JISDelayedSegmentsModel";
|
|
15
|
+
import { AbstractValidatableRepository } from "@golemio/core/dist/helpers/data-access/postgres/repositories/AbstractValidatableRepository.js";
|
|
16
|
+
import { CoreToken } from "@golemio/core/dist/helpers/ioc/CoreToken.js";
|
|
17
|
+
import GolemioErrorsCore from "@golemio/core/dist/shared/golemio-errors.js";
|
|
18
|
+
const { GeneralError } = GolemioErrorsCore;
|
|
19
|
+
import GolemioValidatorCore from "@golemio/core/dist/shared/golemio-validator.js";
|
|
20
|
+
const { JSONSchemaValidator } = GolemioValidatorCore;
|
|
21
|
+
import SequelizeCore from "@golemio/core/dist/shared/sequelize.js";
|
|
22
|
+
const { Op } = SequelizeCore;
|
|
23
|
+
import { inject, injectable } from "@golemio/core/dist/shared/tsyringe.js";
|
|
24
|
+
let JISDelayedSegmentsRepository = class JISDelayedSegmentsRepository extends AbstractValidatableRepository {
|
|
25
|
+
constructor(connector, logger) {
|
|
26
|
+
super(connector, logger);
|
|
27
|
+
this.schema = PG_SCHEMA;
|
|
28
|
+
this.tableName = JISDelayedSegmentsModel.tableName;
|
|
29
|
+
this.validator = new JSONSchemaValidator("JISDelayedSegmentsRepository", JISDelayedSegmentsModel.jsonSchema);
|
|
30
|
+
this.sequelizeModel = connector
|
|
31
|
+
.getConnection()
|
|
32
|
+
.define(this.tableName, JISDelayedSegmentsModel.attributeModel, { schema: this.schema });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Replaces all delayed segments with the given ones. `updated_at` of every given segment is set to `detectedAt`,
|
|
36
|
+
* `created_at` keeps the time the segment was first detected. Segments absent from the given data are deleted.
|
|
37
|
+
*/
|
|
38
|
+
async refreshData(segments, detectedAt) {
|
|
39
|
+
await this.validator.Validate(segments);
|
|
40
|
+
const transaction = await this.connector.getConnection().transaction();
|
|
41
|
+
try {
|
|
42
|
+
const rows = segments.map((segment) => ({ ...segment, created_at: detectedAt, updated_at: detectedAt }));
|
|
43
|
+
await this.sequelizeModel.bulkCreate(rows, {
|
|
44
|
+
updateOnDuplicate: this.getUpdateAttributes(),
|
|
45
|
+
transaction,
|
|
46
|
+
});
|
|
47
|
+
await this.sequelizeModel.destroy({
|
|
48
|
+
where: { updated_at: { [Op.lt]: detectedAt } },
|
|
49
|
+
transaction,
|
|
50
|
+
});
|
|
51
|
+
await transaction.commit();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
await transaction.rollback();
|
|
55
|
+
throw new GeneralError("Error while refreshing delayed segments", this.constructor.name, error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* All attributes except created_at, which keeps the time the segment was first detected
|
|
60
|
+
*/
|
|
61
|
+
getUpdateAttributes() {
|
|
62
|
+
const attributes = Object.keys(JISDelayedSegmentsModel.attributeModel);
|
|
63
|
+
return attributes.filter((attribute) => attribute !== "created_at");
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
JISDelayedSegmentsRepository = __decorate([
|
|
67
|
+
injectable(),
|
|
68
|
+
__param(0, inject(CoreToken.PostgresConnector)),
|
|
69
|
+
__param(1, inject(CoreToken.Logger)),
|
|
70
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
71
|
+
], JISDelayedSegmentsRepository);
|
|
72
|
+
export { JISDelayedSegmentsRepository };
|
|
73
|
+
//# sourceMappingURL=JISDelayedSegmentsRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JISDelayedSegmentsRepository.js","sourceRoot":"","sources":["../../../../src/integration-engine/jis/repositories/JISDelayedSegmentsRepository.ts"],"names":[],"mappings":"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"}
|
package/dist/integration-engine/jis/repositories/helpers/JISDelayedSegmentEnrichmentQuery.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enriches delayed segments given by the ASW ids of their endpoints with their stops, affected routes and info panel
|
|
3
|
+
* stops. All segments of a refresh are enriched by one query, so that planning and lookups shared by the segments
|
|
4
|
+
* are done only once.
|
|
5
|
+
*
|
|
6
|
+
* Replacements: segments (JSON array of objects with segment_index, from_node_id, from_stop_id, to_node_id and
|
|
7
|
+
* to_stop_id), windowStart, windowEnd (departures taken into account) and maxInfoPanelDistanceStops (max distance
|
|
8
|
+
* of an info panel stop from the segment).
|
|
9
|
+
*
|
|
10
|
+
* Returns one row per segment, ordered by segment_index. Stops are matched by ASW id rather than by GTFS stop_id,
|
|
11
|
+
* since one physical stop can have several GTFS stop ids (zone and temporary variants).
|
|
12
|
+
*/
|
|
13
|
+
export declare const JIS_DELAYED_SEGMENT_ENRICHMENT_QUERY = "\nWITH\nsegments AS MATERIALIZED (\n SELECT *\n FROM jsonb_to_recordset(CAST(:segments AS jsonb))\n AS segment(segment_index int, from_node_id int, from_stop_id int, to_node_id int, to_stop_id int)\n),\n-- GTFS stop ids of the segment endpoints, all variants of their ASW ids\nendpoint_stops AS MATERIALIZED (\n SELECT seg.segment_index, TRUE AS is_from, s.stop_id\n FROM segments seg\n JOIN pid.ropidgtfs_stops s ON s.asw_node_id = seg.from_node_id AND s.asw_stop_id = seg.from_stop_id\n UNION ALL\n SELECT seg.segment_index, FALSE AS is_from, s.stop_id\n FROM segments seg\n JOIN pid.ropidgtfs_stops s ON s.asw_node_id = seg.to_node_id AND s.asw_stop_id = seg.to_stop_id\n),\n-- Regular trips in the time window running the whole segment, they define which stops lie on the segment\nsegment_trips AS MATERIALIZED (\n SELECT DISTINCT\n from_stop.segment_index,\n d_from.trip_id,\n d_from.date,\n d_from.stop_sequence AS from_sequence,\n d_to.stop_sequence AS to_sequence\n FROM endpoint_stops from_stop\n JOIN pid.ropidgtfs_precomputed_departures d_from\n ON d_from.stop_id = from_stop.stop_id\n AND d_from.departure_datetime BETWEEN :windowStart AND :windowEnd\n JOIN pid.ropidgtfs_precomputed_departures d_to\n ON d_to.date = d_from.date\n AND d_to.trip_id = d_from.trip_id\n AND d_to.stop_sequence > d_from.stop_sequence\n JOIN endpoint_stops to_stop\n ON to_stop.segment_index = from_stop.segment_index\n AND NOT to_stop.is_from\n AND to_stop.stop_id = d_to.stop_id\n JOIN pid.ropidgtfs_trips t ON t.trip_id = d_from.trip_id AND t.exceptional = 0\n WHERE from_stop.is_from\n),\n-- Stops between the endpoints along each segment trip, with their position relative to the trip's run of the segment\n-- (0 at the start, 1 at the end), so that trips skipping stops agree on the positions of the stops they share\nsegment_trip_stops AS MATERIALIZED (\n SELECT\n st.segment_index,\n st.trip_id,\n st.date,\n d.stop_sequence,\n s.stop_id,\n s.stop_name,\n s.asw_node_id,\n s.asw_stop_id,\n CAST(d.stop_sequence - st.from_sequence AS numeric) / (st.to_sequence - st.from_sequence) AS relative_position\n FROM segment_trips st\n JOIN pid.ropidgtfs_precomputed_departures d\n ON d.date = st.date\n AND d.trip_id = st.trip_id\n AND d.stop_sequence BETWEEN st.from_sequence AND st.to_sequence\n JOIN pid.ropidgtfs_stops s ON s.stop_id = d.stop_id\n WHERE s.asw_node_id IS NOT NULL\n),\n-- One row per segment and physical stop (ASW id), positioned for ordering the stops along the segment. The stop is\n-- represented by the first of all its GTFS stop id variants rather than of those served in the time window, so that\n-- the id stays the same between refreshes, which consumers match segments by.\nsegment_stops AS MATERIALIZED (\n SELECT\n trip_stops.segment_index,\n trip_stops.asw_node_id,\n trip_stops.asw_stop_id,\n MIN(s.stop_id) AS stop_id,\n (ARRAY_AGG(s.stop_name ORDER BY s.stop_id))[1] AS stop_name,\n trip_stops.position\n FROM (\n SELECT segment_index, asw_node_id, asw_stop_id, MIN(relative_position) AS position\n FROM segment_trip_stops\n GROUP BY segment_index, asw_node_id, asw_stop_id\n ) trip_stops\n JOIN pid.ropidgtfs_stops s ON s.asw_node_id = trip_stops.asw_node_id AND s.asw_stop_id = trip_stops.asw_stop_id\n GROUP BY trip_stops.segment_index, trip_stops.asw_node_id, trip_stops.asw_stop_id, trip_stops.position\n),\n-- Ordered pairs of segment stops (\"earlier>later\" by ASW id) visited in this order by some segment trip. Unlike\n-- comparing positions, this holds for any mix of stopping patterns, e.g. a stop placed differently by two branches.\nsegment_stop_precedences AS MATERIALIZED (\n SELECT segment_index, jsonb_object_agg(stop_pair, TRUE) AS precedences\n FROM (\n SELECT DISTINCT pattern.segment_index, earlier.stop_key || '>' || later.stop_key AS stop_pair\n FROM (\n SELECT DISTINCT segment_index, ARRAY_AGG(asw_node_id || '/' || asw_stop_id ORDER BY stop_sequence) AS stop_keys\n FROM segment_trip_stops\n GROUP BY segment_index, trip_id, date\n ) pattern\n CROSS JOIN LATERAL unnest(pattern.stop_keys) WITH ORDINALITY AS earlier(stop_key, stop_order)\n JOIN LATERAL unnest(pattern.stop_keys) WITH ORDINALITY AS later(stop_key, stop_order)\n ON later.stop_order > earlier.stop_order\n ) stop_pairs\n GROUP BY segment_index\n),\n-- All GTFS stop ids of the segment stops, so that trips using any variant are recognized\nsegment_stop_variants AS MATERIALIZED (\n SELECT ss.segment_index, s.stop_id, ss.asw_node_id || '/' || ss.asw_stop_id AS stop_key\n FROM segment_stops ss\n JOIN pid.ropidgtfs_stops s ON s.asw_node_id = ss.asw_node_id AND s.asw_stop_id = ss.asw_stop_id\n),\n-- Departures from the segment stops in the time window, fetched once per stop. Otherwise the planner fetches them\n-- again for every pair of segment stops.\nsegment_stop_departures AS MATERIALIZED (\n SELECT d.trip_id, d.date, d.route_id, d.route_short_name, d.route_type, d.stop_id, d.next_stop_id\n FROM pid.ropidgtfs_precomputed_departures d\n WHERE d.stop_id IN (SELECT stop_id FROM segment_stop_variants)\n AND d.route_id IS NOT NULL\n AND d.departure_datetime BETWEEN :windowStart AND :windowEnd\n),\n-- ASW ids of the segment stop variants and the stop precedences per segment. Looking the stops up in these small maps\n-- instead of joining them keeps the query fast, as the planner heavily underestimates the row counts of the CTEs and\n-- picks nested loops.\nsegment_lookups AS MATERIALIZED (\n SELECT variants.segment_index, variants.stop_keys, ssp.precedences\n FROM (\n SELECT segment_index, jsonb_object_agg(stop_id, stop_key) AS stop_keys\n FROM segment_stop_variants\n GROUP BY segment_index\n ) variants\n JOIN segment_stop_precedences ssp ON ssp.segment_index = variants.segment_index\n),\n-- Trips in the time window travelling at least one hop between two segment stops in the segment direction,\n-- unlike segment trips they may join or leave the segment in its middle\naffected_trip_candidates AS MATERIALIZED (\n SELECT DISTINCT current_stop.segment_index, d.trip_id, d.date, d.route_id, d.route_short_name, d.route_type\n FROM segment_stop_departures d\n JOIN segment_stop_variants current_stop ON current_stop.stop_id = d.stop_id\n JOIN segment_lookups sl ON sl.segment_index = current_stop.segment_index\n WHERE (sl.precedences -> (current_stop.stop_key || '>' || (sl.stop_keys ->> d.next_stop_id))) IS NOT NULL\n),\n-- Regular affected trips, with the segment stops for the lookups below\naffected_trips AS MATERIALIZED (\n SELECT atc.*, sl.stop_keys AS segment_stop_keys\n FROM affected_trip_candidates atc\n JOIN segment_lookups sl ON sl.segment_index = atc.segment_index\n WHERE EXISTS (SELECT 1 FROM pid.ropidgtfs_trips t WHERE t.trip_id = atc.trip_id AND t.exceptional = 0)\n),\n-- Stops with a departure preset (info panels) by each GTFS stop id variant of their ASW id. Links to presets that\n-- no longer exist are ignored (the tables are refreshed separately and have no foreign key).\npreset_stop_variants AS MATERIALIZED (\n SELECT DISTINCT variant.stop_id AS variant_stop_id, preset_stop.stop_id AS preset_stop_id\n FROM pid.ropid_departures_presets_ropidgtfs_stops ps\n JOIN pid.ropid_departures_presets p ON p.id = ps.preset_id\n JOIN pid.ropidgtfs_stops preset_stop ON preset_stop.stop_id = ps.stop_id\n JOIN pid.ropidgtfs_stops variant\n ON variant.asw_node_id = preset_stop.asw_node_id AND variant.asw_stop_id = preset_stop.asw_stop_id\n),\n-- First and last stop sequence of each affected trip on the segment.\n-- Distances in stops are computed as differences of stop sequences, which relies on the stop sequences of a trip\n-- being consecutive (true for all PID trips). Ranking all stops of all affected trips instead takes most of the\n-- query time. Should there be gaps, a distance would grow by the gap size.\naffected_trip_bounds AS MATERIALIZED (\n SELECT\n at.segment_index,\n at.trip_id,\n at.date,\n at.route_id,\n MIN(d.stop_sequence) AS first_sequence,\n MAX(d.stop_sequence) AS last_sequence\n FROM affected_trips at\n JOIN pid.ropidgtfs_precomputed_departures d ON d.date = at.date AND d.trip_id = at.trip_id\n WHERE (at.segment_stop_keys -> d.stop_id) IS NOT NULL\n GROUP BY at.segment_index, at.trip_id, at.date, at.route_id\n),\n-- Distance of each preset stop from the segment per affected route, up to the max distance: negative before\n-- the segment, 0 on it, positive after it. Of the distances on different trips of a route, the closest is kept.\npreset_stop_distances AS MATERIALIZED (\n SELECT DISTINCT ON (segment_index, stop_id, route_id) segment_index, stop_id, route_id, distance\n FROM (\n SELECT\n atb.segment_index,\n preset_stop.preset_stop_id AS stop_id,\n atb.route_id,\n CASE\n WHEN d.stop_sequence < atb.first_sequence THEN d.stop_sequence - atb.first_sequence\n WHEN d.stop_sequence > atb.last_sequence THEN d.stop_sequence - atb.last_sequence\n ELSE 0\n END AS distance\n FROM affected_trip_bounds atb\n JOIN pid.ropidgtfs_precomputed_departures d\n ON d.date = atb.date\n AND d.trip_id = atb.trip_id\n AND d.stop_sequence BETWEEN atb.first_sequence - :maxInfoPanelDistanceStops\n AND atb.last_sequence + :maxInfoPanelDistanceStops\n JOIN preset_stop_variants preset_stop ON preset_stop.variant_stop_id = d.stop_id\n ) distances\n ORDER BY segment_index, stop_id, route_id, ABS(distance), distance\n)\n-- One row per segment with the enrichment as JSON: endpoints, ordered segment stops, affected routes and info panel stops\nSELECT\n (\n SELECT COUNT(DISTINCT es.is_from) = 2\n FROM endpoint_stops es\n WHERE es.segment_index = seg.segment_index\n ) AS has_endpoint_stops,\n (\n SELECT json_build_object('id', ss.stop_id, 'name', ss.stop_name)\n FROM segment_stops ss\n WHERE ss.segment_index = seg.segment_index\n AND ss.asw_node_id = seg.from_node_id\n AND ss.asw_stop_id = seg.from_stop_id\n ) AS from_stop,\n (\n SELECT json_build_object('id', ss.stop_id, 'name', ss.stop_name)\n FROM segment_stops ss\n WHERE ss.segment_index = seg.segment_index\n AND ss.asw_node_id = seg.to_node_id\n AND ss.asw_stop_id = seg.to_stop_id\n ) AS to_stop,\n (\n SELECT COALESCE(json_agg(\n json_build_object('id', ss.stop_id, 'name', ss.stop_name) ORDER BY ss.position, ss.stop_id\n ), '[]'::json)\n FROM segment_stops ss\n WHERE ss.segment_index = seg.segment_index\n ) AS stops,\n (\n SELECT COALESCE(json_agg(json_build_object(\n 'id', r.route_id,\n 'short_name', r.route_short_name,\n 'type', r.route_type\n ) ORDER BY r.route_id), '[]'::json)\n FROM (\n SELECT DISTINCT route_id, route_short_name, route_type\n FROM affected_trips\n WHERE segment_index = seg.segment_index\n ) r\n ) AS routes,\n (\n SELECT COALESCE(json_agg(json_build_object(\n 'stop', json_build_object('id', s.stop_id, 'name', s.stop_name),\n 'preset_route_names', (\n SELECT json_agg(DISTINCT p.route_name ORDER BY p.route_name)\n FROM pid.ropid_departures_presets_ropidgtfs_stops ps\n JOIN pid.ropid_departures_presets p ON p.id = ps.preset_id\n WHERE ps.stop_id = s.stop_id\n ),\n 'routes', (\n SELECT json_agg(json_build_object('id', psd.route_id, 'distance_stops', psd.distance) ORDER BY psd.route_id)\n FROM preset_stop_distances psd\n WHERE psd.segment_index = seg.segment_index AND psd.stop_id = s.stop_id\n )\n ) ORDER BY s.stop_id), '[]'::json)\n FROM pid.ropidgtfs_stops s\n WHERE s.stop_id IN (SELECT psd.stop_id FROM preset_stop_distances psd WHERE psd.segment_index = seg.segment_index)\n ) AS info_panel_stops\nFROM segments seg\nORDER BY seg.segment_index\n";
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { PG_SCHEMA } from "#sch/const";
|
|
2
|
+
/**
|
|
3
|
+
* Enriches delayed segments given by the ASW ids of their endpoints with their stops, affected routes and info panel
|
|
4
|
+
* stops. All segments of a refresh are enriched by one query, so that planning and lookups shared by the segments
|
|
5
|
+
* are done only once.
|
|
6
|
+
*
|
|
7
|
+
* Replacements: segments (JSON array of objects with segment_index, from_node_id, from_stop_id, to_node_id and
|
|
8
|
+
* to_stop_id), windowStart, windowEnd (departures taken into account) and maxInfoPanelDistanceStops (max distance
|
|
9
|
+
* of an info panel stop from the segment).
|
|
10
|
+
*
|
|
11
|
+
* Returns one row per segment, ordered by segment_index. Stops are matched by ASW id rather than by GTFS stop_id,
|
|
12
|
+
* since one physical stop can have several GTFS stop ids (zone and temporary variants).
|
|
13
|
+
*/
|
|
14
|
+
export const JIS_DELAYED_SEGMENT_ENRICHMENT_QUERY = `
|
|
15
|
+
WITH
|
|
16
|
+
segments AS MATERIALIZED (
|
|
17
|
+
SELECT *
|
|
18
|
+
FROM jsonb_to_recordset(CAST(:segments AS jsonb))
|
|
19
|
+
AS segment(segment_index int, from_node_id int, from_stop_id int, to_node_id int, to_stop_id int)
|
|
20
|
+
),
|
|
21
|
+
-- GTFS stop ids of the segment endpoints, all variants of their ASW ids
|
|
22
|
+
endpoint_stops AS MATERIALIZED (
|
|
23
|
+
SELECT seg.segment_index, TRUE AS is_from, s.stop_id
|
|
24
|
+
FROM segments seg
|
|
25
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops s ON s.asw_node_id = seg.from_node_id AND s.asw_stop_id = seg.from_stop_id
|
|
26
|
+
UNION ALL
|
|
27
|
+
SELECT seg.segment_index, FALSE AS is_from, s.stop_id
|
|
28
|
+
FROM segments seg
|
|
29
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops s ON s.asw_node_id = seg.to_node_id AND s.asw_stop_id = seg.to_stop_id
|
|
30
|
+
),
|
|
31
|
+
-- Regular trips in the time window running the whole segment, they define which stops lie on the segment
|
|
32
|
+
segment_trips AS MATERIALIZED (
|
|
33
|
+
SELECT DISTINCT
|
|
34
|
+
from_stop.segment_index,
|
|
35
|
+
d_from.trip_id,
|
|
36
|
+
d_from.date,
|
|
37
|
+
d_from.stop_sequence AS from_sequence,
|
|
38
|
+
d_to.stop_sequence AS to_sequence
|
|
39
|
+
FROM endpoint_stops from_stop
|
|
40
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_precomputed_departures d_from
|
|
41
|
+
ON d_from.stop_id = from_stop.stop_id
|
|
42
|
+
AND d_from.departure_datetime BETWEEN :windowStart AND :windowEnd
|
|
43
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_precomputed_departures d_to
|
|
44
|
+
ON d_to.date = d_from.date
|
|
45
|
+
AND d_to.trip_id = d_from.trip_id
|
|
46
|
+
AND d_to.stop_sequence > d_from.stop_sequence
|
|
47
|
+
JOIN endpoint_stops to_stop
|
|
48
|
+
ON to_stop.segment_index = from_stop.segment_index
|
|
49
|
+
AND NOT to_stop.is_from
|
|
50
|
+
AND to_stop.stop_id = d_to.stop_id
|
|
51
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_trips t ON t.trip_id = d_from.trip_id AND t.exceptional = 0
|
|
52
|
+
WHERE from_stop.is_from
|
|
53
|
+
),
|
|
54
|
+
-- Stops between the endpoints along each segment trip, with their position relative to the trip's run of the segment
|
|
55
|
+
-- (0 at the start, 1 at the end), so that trips skipping stops agree on the positions of the stops they share
|
|
56
|
+
segment_trip_stops AS MATERIALIZED (
|
|
57
|
+
SELECT
|
|
58
|
+
st.segment_index,
|
|
59
|
+
st.trip_id,
|
|
60
|
+
st.date,
|
|
61
|
+
d.stop_sequence,
|
|
62
|
+
s.stop_id,
|
|
63
|
+
s.stop_name,
|
|
64
|
+
s.asw_node_id,
|
|
65
|
+
s.asw_stop_id,
|
|
66
|
+
CAST(d.stop_sequence - st.from_sequence AS numeric) / (st.to_sequence - st.from_sequence) AS relative_position
|
|
67
|
+
FROM segment_trips st
|
|
68
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_precomputed_departures d
|
|
69
|
+
ON d.date = st.date
|
|
70
|
+
AND d.trip_id = st.trip_id
|
|
71
|
+
AND d.stop_sequence BETWEEN st.from_sequence AND st.to_sequence
|
|
72
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops s ON s.stop_id = d.stop_id
|
|
73
|
+
WHERE s.asw_node_id IS NOT NULL
|
|
74
|
+
),
|
|
75
|
+
-- One row per segment and physical stop (ASW id), positioned for ordering the stops along the segment. The stop is
|
|
76
|
+
-- represented by the first of all its GTFS stop id variants rather than of those served in the time window, so that
|
|
77
|
+
-- the id stays the same between refreshes, which consumers match segments by.
|
|
78
|
+
segment_stops AS MATERIALIZED (
|
|
79
|
+
SELECT
|
|
80
|
+
trip_stops.segment_index,
|
|
81
|
+
trip_stops.asw_node_id,
|
|
82
|
+
trip_stops.asw_stop_id,
|
|
83
|
+
MIN(s.stop_id) AS stop_id,
|
|
84
|
+
(ARRAY_AGG(s.stop_name ORDER BY s.stop_id))[1] AS stop_name,
|
|
85
|
+
trip_stops.position
|
|
86
|
+
FROM (
|
|
87
|
+
SELECT segment_index, asw_node_id, asw_stop_id, MIN(relative_position) AS position
|
|
88
|
+
FROM segment_trip_stops
|
|
89
|
+
GROUP BY segment_index, asw_node_id, asw_stop_id
|
|
90
|
+
) trip_stops
|
|
91
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops s ON s.asw_node_id = trip_stops.asw_node_id AND s.asw_stop_id = trip_stops.asw_stop_id
|
|
92
|
+
GROUP BY trip_stops.segment_index, trip_stops.asw_node_id, trip_stops.asw_stop_id, trip_stops.position
|
|
93
|
+
),
|
|
94
|
+
-- Ordered pairs of segment stops ("earlier>later" by ASW id) visited in this order by some segment trip. Unlike
|
|
95
|
+
-- comparing positions, this holds for any mix of stopping patterns, e.g. a stop placed differently by two branches.
|
|
96
|
+
segment_stop_precedences AS MATERIALIZED (
|
|
97
|
+
SELECT segment_index, jsonb_object_agg(stop_pair, TRUE) AS precedences
|
|
98
|
+
FROM (
|
|
99
|
+
SELECT DISTINCT pattern.segment_index, earlier.stop_key || '>' || later.stop_key AS stop_pair
|
|
100
|
+
FROM (
|
|
101
|
+
SELECT DISTINCT segment_index, ARRAY_AGG(asw_node_id || '/' || asw_stop_id ORDER BY stop_sequence) AS stop_keys
|
|
102
|
+
FROM segment_trip_stops
|
|
103
|
+
GROUP BY segment_index, trip_id, date
|
|
104
|
+
) pattern
|
|
105
|
+
CROSS JOIN LATERAL unnest(pattern.stop_keys) WITH ORDINALITY AS earlier(stop_key, stop_order)
|
|
106
|
+
JOIN LATERAL unnest(pattern.stop_keys) WITH ORDINALITY AS later(stop_key, stop_order)
|
|
107
|
+
ON later.stop_order > earlier.stop_order
|
|
108
|
+
) stop_pairs
|
|
109
|
+
GROUP BY segment_index
|
|
110
|
+
),
|
|
111
|
+
-- All GTFS stop ids of the segment stops, so that trips using any variant are recognized
|
|
112
|
+
segment_stop_variants AS MATERIALIZED (
|
|
113
|
+
SELECT ss.segment_index, s.stop_id, ss.asw_node_id || '/' || ss.asw_stop_id AS stop_key
|
|
114
|
+
FROM segment_stops ss
|
|
115
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops s ON s.asw_node_id = ss.asw_node_id AND s.asw_stop_id = ss.asw_stop_id
|
|
116
|
+
),
|
|
117
|
+
-- Departures from the segment stops in the time window, fetched once per stop. Otherwise the planner fetches them
|
|
118
|
+
-- again for every pair of segment stops.
|
|
119
|
+
segment_stop_departures AS MATERIALIZED (
|
|
120
|
+
SELECT d.trip_id, d.date, d.route_id, d.route_short_name, d.route_type, d.stop_id, d.next_stop_id
|
|
121
|
+
FROM ${PG_SCHEMA}.ropidgtfs_precomputed_departures d
|
|
122
|
+
WHERE d.stop_id IN (SELECT stop_id FROM segment_stop_variants)
|
|
123
|
+
AND d.route_id IS NOT NULL
|
|
124
|
+
AND d.departure_datetime BETWEEN :windowStart AND :windowEnd
|
|
125
|
+
),
|
|
126
|
+
-- ASW ids of the segment stop variants and the stop precedences per segment. Looking the stops up in these small maps
|
|
127
|
+
-- instead of joining them keeps the query fast, as the planner heavily underestimates the row counts of the CTEs and
|
|
128
|
+
-- picks nested loops.
|
|
129
|
+
segment_lookups AS MATERIALIZED (
|
|
130
|
+
SELECT variants.segment_index, variants.stop_keys, ssp.precedences
|
|
131
|
+
FROM (
|
|
132
|
+
SELECT segment_index, jsonb_object_agg(stop_id, stop_key) AS stop_keys
|
|
133
|
+
FROM segment_stop_variants
|
|
134
|
+
GROUP BY segment_index
|
|
135
|
+
) variants
|
|
136
|
+
JOIN segment_stop_precedences ssp ON ssp.segment_index = variants.segment_index
|
|
137
|
+
),
|
|
138
|
+
-- Trips in the time window travelling at least one hop between two segment stops in the segment direction,
|
|
139
|
+
-- unlike segment trips they may join or leave the segment in its middle
|
|
140
|
+
affected_trip_candidates AS MATERIALIZED (
|
|
141
|
+
SELECT DISTINCT current_stop.segment_index, d.trip_id, d.date, d.route_id, d.route_short_name, d.route_type
|
|
142
|
+
FROM segment_stop_departures d
|
|
143
|
+
JOIN segment_stop_variants current_stop ON current_stop.stop_id = d.stop_id
|
|
144
|
+
JOIN segment_lookups sl ON sl.segment_index = current_stop.segment_index
|
|
145
|
+
WHERE (sl.precedences -> (current_stop.stop_key || '>' || (sl.stop_keys ->> d.next_stop_id))) IS NOT NULL
|
|
146
|
+
),
|
|
147
|
+
-- Regular affected trips, with the segment stops for the lookups below
|
|
148
|
+
affected_trips AS MATERIALIZED (
|
|
149
|
+
SELECT atc.*, sl.stop_keys AS segment_stop_keys
|
|
150
|
+
FROM affected_trip_candidates atc
|
|
151
|
+
JOIN segment_lookups sl ON sl.segment_index = atc.segment_index
|
|
152
|
+
WHERE EXISTS (SELECT 1 FROM ${PG_SCHEMA}.ropidgtfs_trips t WHERE t.trip_id = atc.trip_id AND t.exceptional = 0)
|
|
153
|
+
),
|
|
154
|
+
-- Stops with a departure preset (info panels) by each GTFS stop id variant of their ASW id. Links to presets that
|
|
155
|
+
-- no longer exist are ignored (the tables are refreshed separately and have no foreign key).
|
|
156
|
+
preset_stop_variants AS MATERIALIZED (
|
|
157
|
+
SELECT DISTINCT variant.stop_id AS variant_stop_id, preset_stop.stop_id AS preset_stop_id
|
|
158
|
+
FROM ${PG_SCHEMA}.ropid_departures_presets_ropidgtfs_stops ps
|
|
159
|
+
JOIN ${PG_SCHEMA}.ropid_departures_presets p ON p.id = ps.preset_id
|
|
160
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops preset_stop ON preset_stop.stop_id = ps.stop_id
|
|
161
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_stops variant
|
|
162
|
+
ON variant.asw_node_id = preset_stop.asw_node_id AND variant.asw_stop_id = preset_stop.asw_stop_id
|
|
163
|
+
),
|
|
164
|
+
-- First and last stop sequence of each affected trip on the segment.
|
|
165
|
+
-- Distances in stops are computed as differences of stop sequences, which relies on the stop sequences of a trip
|
|
166
|
+
-- being consecutive (true for all PID trips). Ranking all stops of all affected trips instead takes most of the
|
|
167
|
+
-- query time. Should there be gaps, a distance would grow by the gap size.
|
|
168
|
+
affected_trip_bounds AS MATERIALIZED (
|
|
169
|
+
SELECT
|
|
170
|
+
at.segment_index,
|
|
171
|
+
at.trip_id,
|
|
172
|
+
at.date,
|
|
173
|
+
at.route_id,
|
|
174
|
+
MIN(d.stop_sequence) AS first_sequence,
|
|
175
|
+
MAX(d.stop_sequence) AS last_sequence
|
|
176
|
+
FROM affected_trips at
|
|
177
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_precomputed_departures d ON d.date = at.date AND d.trip_id = at.trip_id
|
|
178
|
+
WHERE (at.segment_stop_keys -> d.stop_id) IS NOT NULL
|
|
179
|
+
GROUP BY at.segment_index, at.trip_id, at.date, at.route_id
|
|
180
|
+
),
|
|
181
|
+
-- Distance of each preset stop from the segment per affected route, up to the max distance: negative before
|
|
182
|
+
-- the segment, 0 on it, positive after it. Of the distances on different trips of a route, the closest is kept.
|
|
183
|
+
preset_stop_distances AS MATERIALIZED (
|
|
184
|
+
SELECT DISTINCT ON (segment_index, stop_id, route_id) segment_index, stop_id, route_id, distance
|
|
185
|
+
FROM (
|
|
186
|
+
SELECT
|
|
187
|
+
atb.segment_index,
|
|
188
|
+
preset_stop.preset_stop_id AS stop_id,
|
|
189
|
+
atb.route_id,
|
|
190
|
+
CASE
|
|
191
|
+
WHEN d.stop_sequence < atb.first_sequence THEN d.stop_sequence - atb.first_sequence
|
|
192
|
+
WHEN d.stop_sequence > atb.last_sequence THEN d.stop_sequence - atb.last_sequence
|
|
193
|
+
ELSE 0
|
|
194
|
+
END AS distance
|
|
195
|
+
FROM affected_trip_bounds atb
|
|
196
|
+
JOIN ${PG_SCHEMA}.ropidgtfs_precomputed_departures d
|
|
197
|
+
ON d.date = atb.date
|
|
198
|
+
AND d.trip_id = atb.trip_id
|
|
199
|
+
AND d.stop_sequence BETWEEN atb.first_sequence - :maxInfoPanelDistanceStops
|
|
200
|
+
AND atb.last_sequence + :maxInfoPanelDistanceStops
|
|
201
|
+
JOIN preset_stop_variants preset_stop ON preset_stop.variant_stop_id = d.stop_id
|
|
202
|
+
) distances
|
|
203
|
+
ORDER BY segment_index, stop_id, route_id, ABS(distance), distance
|
|
204
|
+
)
|
|
205
|
+
-- One row per segment with the enrichment as JSON: endpoints, ordered segment stops, affected routes and info panel stops
|
|
206
|
+
SELECT
|
|
207
|
+
(
|
|
208
|
+
SELECT COUNT(DISTINCT es.is_from) = 2
|
|
209
|
+
FROM endpoint_stops es
|
|
210
|
+
WHERE es.segment_index = seg.segment_index
|
|
211
|
+
) AS has_endpoint_stops,
|
|
212
|
+
(
|
|
213
|
+
SELECT json_build_object('id', ss.stop_id, 'name', ss.stop_name)
|
|
214
|
+
FROM segment_stops ss
|
|
215
|
+
WHERE ss.segment_index = seg.segment_index
|
|
216
|
+
AND ss.asw_node_id = seg.from_node_id
|
|
217
|
+
AND ss.asw_stop_id = seg.from_stop_id
|
|
218
|
+
) AS from_stop,
|
|
219
|
+
(
|
|
220
|
+
SELECT json_build_object('id', ss.stop_id, 'name', ss.stop_name)
|
|
221
|
+
FROM segment_stops ss
|
|
222
|
+
WHERE ss.segment_index = seg.segment_index
|
|
223
|
+
AND ss.asw_node_id = seg.to_node_id
|
|
224
|
+
AND ss.asw_stop_id = seg.to_stop_id
|
|
225
|
+
) AS to_stop,
|
|
226
|
+
(
|
|
227
|
+
SELECT COALESCE(json_agg(
|
|
228
|
+
json_build_object('id', ss.stop_id, 'name', ss.stop_name) ORDER BY ss.position, ss.stop_id
|
|
229
|
+
), '[]'::json)
|
|
230
|
+
FROM segment_stops ss
|
|
231
|
+
WHERE ss.segment_index = seg.segment_index
|
|
232
|
+
) AS stops,
|
|
233
|
+
(
|
|
234
|
+
SELECT COALESCE(json_agg(json_build_object(
|
|
235
|
+
'id', r.route_id,
|
|
236
|
+
'short_name', r.route_short_name,
|
|
237
|
+
'type', r.route_type
|
|
238
|
+
) ORDER BY r.route_id), '[]'::json)
|
|
239
|
+
FROM (
|
|
240
|
+
SELECT DISTINCT route_id, route_short_name, route_type
|
|
241
|
+
FROM affected_trips
|
|
242
|
+
WHERE segment_index = seg.segment_index
|
|
243
|
+
) r
|
|
244
|
+
) AS routes,
|
|
245
|
+
(
|
|
246
|
+
SELECT COALESCE(json_agg(json_build_object(
|
|
247
|
+
'stop', json_build_object('id', s.stop_id, 'name', s.stop_name),
|
|
248
|
+
'preset_route_names', (
|
|
249
|
+
SELECT json_agg(DISTINCT p.route_name ORDER BY p.route_name)
|
|
250
|
+
FROM ${PG_SCHEMA}.ropid_departures_presets_ropidgtfs_stops ps
|
|
251
|
+
JOIN ${PG_SCHEMA}.ropid_departures_presets p ON p.id = ps.preset_id
|
|
252
|
+
WHERE ps.stop_id = s.stop_id
|
|
253
|
+
),
|
|
254
|
+
'routes', (
|
|
255
|
+
SELECT json_agg(json_build_object('id', psd.route_id, 'distance_stops', psd.distance) ORDER BY psd.route_id)
|
|
256
|
+
FROM preset_stop_distances psd
|
|
257
|
+
WHERE psd.segment_index = seg.segment_index AND psd.stop_id = s.stop_id
|
|
258
|
+
)
|
|
259
|
+
) ORDER BY s.stop_id), '[]'::json)
|
|
260
|
+
FROM ${PG_SCHEMA}.ropidgtfs_stops s
|
|
261
|
+
WHERE s.stop_id IN (SELECT psd.stop_id FROM preset_stop_distances psd WHERE psd.segment_index = seg.segment_index)
|
|
262
|
+
) AS info_panel_stops
|
|
263
|
+
FROM segments seg
|
|
264
|
+
ORDER BY seg.segment_index
|
|
265
|
+
`;
|
|
266
|
+
//# sourceMappingURL=JISDelayedSegmentEnrichmentQuery.js.map
|
package/dist/integration-engine/jis/repositories/helpers/JISDelayedSegmentEnrichmentQuery.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JISDelayedSegmentEnrichmentQuery.js","sourceRoot":"","sources":["../../../../../src/integration-engine/jis/repositories/helpers/JISDelayedSegmentEnrichmentQuery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAAG;;;;;;;;;;;WAWzC,SAAS;;;;WAIT,SAAS;;;;;;;;;;;WAWT,SAAS;;;WAGT,SAAS;;;;;;;;WAQT,SAAS;;;;;;;;;;;;;;;;;WAiBT,SAAS;;;;WAIT,SAAS;;;;;;;;;;;;;;;;;;;WAmBT,SAAS;;;;;;;;;;;;;;;;;;;;;;;;WAwBT,SAAS;;;;;;WAMT,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCA+Bc,SAAS;;;;;;WAMhC,SAAS;WACT,SAAS;WACT,SAAS;WACT,SAAS;;;;;;;;;;;;;;;;WAgBT,SAAS;;;;;;;;;;;;;;;;;;;eAmBL,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAsDD,SAAS;uBACT,SAAS;;;;;;;;;eASjB,SAAS;;;;;CAKvB,CAAC"}
|
package/dist/integration-engine/jis/repositories/interfaces/IJISDelayedSegmentEnrichmentParams.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ASW id of a stop: node (uzel) and stop (sloupek)
|
|
3
|
+
*/
|
|
4
|
+
export interface IAswStopId {
|
|
5
|
+
nodeId: number;
|
|
6
|
+
stopId: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Endpoints of a delayed segment as ASW ids
|
|
10
|
+
*/
|
|
11
|
+
export interface IAswSegmentEndpoints {
|
|
12
|
+
from: IAswStopId;
|
|
13
|
+
to: IAswStopId;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Delayed segments to enrich and the parameters of the enrichment
|
|
17
|
+
*/
|
|
18
|
+
export interface IJISDelayedSegmentEnrichmentParams {
|
|
19
|
+
segments: IAswSegmentEndpoints[];
|
|
20
|
+
windowStart: Date;
|
|
21
|
+
windowEnd: Date;
|
|
22
|
+
maxInfoPanelDistanceStops: number;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IJISDelayedSegmentEnrichmentParams.js","sourceRoot":"","sources":["../../../../../src/integration-engine/jis/repositories/interfaces/IJISDelayedSegmentEnrichmentParams.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JISDelayedSegmentEnrichmentRepository } from "#ie/jis/repositories/JISDelayedSegmentEnrichmentRepository";
|
|
2
|
+
import { JISDelayedSegmentsRepository } from "#ie/jis/repositories/JISDelayedSegmentsRepository";
|
|
3
|
+
import { type IMapaPidDelayShape } from "#sch/jis/datasources/interfaces/index";
|
|
4
|
+
import { type ISimpleConfig } from "@golemio/core/dist/helpers/configuration/ISimpleConfig.js";
|
|
5
|
+
import { type ILogger } from "@golemio/core/dist/helpers/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Turns delayed segments detected by Mapa PID into delayed segments enriched with GTFS data and stores them
|
|
8
|
+
*/
|
|
9
|
+
export declare class JISDelayedSegmentsDataService {
|
|
10
|
+
private enrichmentRepository;
|
|
11
|
+
private delayedSegmentsRepository;
|
|
12
|
+
private logger;
|
|
13
|
+
private readonly minDelaySeconds;
|
|
14
|
+
private readonly routeTimeWindowMinutes;
|
|
15
|
+
private readonly maxInfoPanelDistanceStops;
|
|
16
|
+
constructor(enrichmentRepository: JISDelayedSegmentEnrichmentRepository, delayedSegmentsRepository: JISDelayedSegmentsRepository, config: ISimpleConfig, logger: ILogger);
|
|
17
|
+
/**
|
|
18
|
+
* Replaces stored delayed segments with the given detected ones. Segments below the minimum delay, segments whose
|
|
19
|
+
* endpoints cannot be mapped to GTFS stops, segments no trip runs as a whole and segments without any affected
|
|
20
|
+
* route are skipped and counted by reason.
|
|
21
|
+
*/
|
|
22
|
+
refreshDelayedSegments(delayShapes: IMapaPidDelayShape[], detectedAt: Date): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Significant delay shapes with endpoints mapped to ASW ids
|
|
25
|
+
*/
|
|
26
|
+
private getCandidates;
|
|
27
|
+
/**
|
|
28
|
+
* Enrichments of the candidates by segment id, derived once per segment
|
|
29
|
+
*/
|
|
30
|
+
private getEnrichments;
|
|
31
|
+
private parseAswStopId;
|
|
32
|
+
private isValidAswIdPart;
|
|
33
|
+
/**
|
|
34
|
+
* Skipped segments are logged only at the debug level, as they repeat every refresh while the delay lasts
|
|
35
|
+
*/
|
|
36
|
+
private skip;
|
|
37
|
+
private toDelayedSegment;
|
|
38
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { GtfsStopParser } from "#helpers/GtfsStopParser";
|
|
14
|
+
import { JISContainerToken } from "#ie/jis/ioc/JISContainerToken";
|
|
15
|
+
import { JISDelayedSegmentEnrichmentRepository } from "#ie/jis/repositories/JISDelayedSegmentEnrichmentRepository";
|
|
16
|
+
import { JISDelayedSegmentsRepository } from "#ie/jis/repositories/JISDelayedSegmentsRepository";
|
|
17
|
+
import { CoreToken } from "@golemio/core/dist/helpers/ioc/CoreToken.js";
|
|
18
|
+
import GolemioErrorsCore from "@golemio/core/dist/shared/golemio-errors.js";
|
|
19
|
+
const { GeneralError } = GolemioErrorsCore;
|
|
20
|
+
import { inject, injectable } from "@golemio/core/dist/shared/tsyringe.js";
|
|
21
|
+
import { PG_INT_MAX } from "../../../const.js";
|
|
22
|
+
const MINUTE_IN_MS = 60 * 1000;
|
|
23
|
+
var SkipReason;
|
|
24
|
+
(function (SkipReason) {
|
|
25
|
+
SkipReason["BelowMinimumDelay"] = "below minimum delay";
|
|
26
|
+
SkipReason["NoAswIds"] = "no ASW ids";
|
|
27
|
+
SkipReason["EndpointStopsNotInGtfs"] = "endpoint stops not in GTFS";
|
|
28
|
+
// Mapa PID merges adjacent delayed sections regardless of the lines running them
|
|
29
|
+
SkipReason["NoTripRunsWholeSegment"] = "no trip runs the whole segment";
|
|
30
|
+
SkipReason["NoAffectedRoutes"] = "no affected routes";
|
|
31
|
+
})(SkipReason || (SkipReason = {}));
|
|
32
|
+
/**
|
|
33
|
+
* Turns delayed segments detected by Mapa PID into delayed segments enriched with GTFS data and stores them
|
|
34
|
+
*/
|
|
35
|
+
let JISDelayedSegmentsDataService = class JISDelayedSegmentsDataService {
|
|
36
|
+
constructor(enrichmentRepository, delayedSegmentsRepository, config, logger) {
|
|
37
|
+
this.enrichmentRepository = enrichmentRepository;
|
|
38
|
+
this.delayedSegmentsRepository = delayedSegmentsRepository;
|
|
39
|
+
this.logger = logger;
|
|
40
|
+
this.minDelaySeconds = Number(config.getValue("module.pid.jis.delayedSegments.minDelaySeconds", 300));
|
|
41
|
+
this.routeTimeWindowMinutes = Number(config.getValue("module.pid.jis.delayedSegments.routeTimeWindowMinutes", 120));
|
|
42
|
+
this.maxInfoPanelDistanceStops = Number(config.getValue("module.pid.jis.delayedSegments.maxInfoPanelDistanceStops", 5));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Replaces stored delayed segments with the given detected ones. Segments below the minimum delay, segments whose
|
|
46
|
+
* endpoints cannot be mapped to GTFS stops, segments no trip runs as a whole and segments without any affected
|
|
47
|
+
* route are skipped and counted by reason.
|
|
48
|
+
*/
|
|
49
|
+
async refreshDelayedSegments(delayShapes, detectedAt) {
|
|
50
|
+
const skipCounts = new Map();
|
|
51
|
+
const candidates = this.getCandidates(delayShapes, skipCounts);
|
|
52
|
+
const enrichments = await this.getEnrichments(candidates, detectedAt);
|
|
53
|
+
const segments = new Map();
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
const enrichment = enrichments.get(candidate.id);
|
|
56
|
+
if (!enrichment) {
|
|
57
|
+
throw new GeneralError(`Missing enrichment of delayed segment ${candidate.id}`, this.constructor.name);
|
|
58
|
+
}
|
|
59
|
+
const segment = this.toDelayedSegment(candidate, enrichment);
|
|
60
|
+
if (typeof segment === "string") {
|
|
61
|
+
this.skip(skipCounts, segment, candidate.id);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// stop id variants of one physical stop may result in duplicate segments, the biggest delay wins
|
|
65
|
+
const duplicate = segments.get(segment.id);
|
|
66
|
+
if (!duplicate || duplicate.expected_delay_seconds < segment.expected_delay_seconds) {
|
|
67
|
+
segments.set(segment.id, segment);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
await this.delayedSegmentsRepository.refreshData(Array.from(segments.values()), detectedAt);
|
|
71
|
+
const skipped = Array.from(skipCounts, ([reason, count]) => `${reason} ${count}`).join(", ") || "none";
|
|
72
|
+
this.logger.info(`${this.constructor.name}: stored ${segments.size} of ${delayShapes.length} delayed segments, skipped: ${skipped}.`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Significant delay shapes with endpoints mapped to ASW ids
|
|
76
|
+
*/
|
|
77
|
+
getCandidates(delayShapes, skipCounts) {
|
|
78
|
+
const candidates = [];
|
|
79
|
+
for (const delayShape of delayShapes) {
|
|
80
|
+
if (delayShape.expected_delay < this.minDelaySeconds) {
|
|
81
|
+
skipCounts.set(SkipReason.BelowMinimumDelay, (skipCounts.get(SkipReason.BelowMinimumDelay) ?? 0) + 1);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const from = this.parseAswStopId(delayShape.from_stop_id);
|
|
85
|
+
const to = this.parseAswStopId(delayShape.to_stop_id);
|
|
86
|
+
if (!from || !to) {
|
|
87
|
+
this.skip(skipCounts, SkipReason.NoAswIds, `${delayShape.from_stop_id} -> ${delayShape.to_stop_id}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
candidates.push({
|
|
91
|
+
id: `${from.nodeId}/${from.stopId}-${to.nodeId}/${to.stopId}`,
|
|
92
|
+
delayShape,
|
|
93
|
+
endpoints: { from, to },
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return candidates;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Enrichments of the candidates by segment id, derived once per segment
|
|
100
|
+
*/
|
|
101
|
+
async getEnrichments(candidates, detectedAt) {
|
|
102
|
+
const endpointsToEnrich = new Map();
|
|
103
|
+
for (const candidate of candidates) {
|
|
104
|
+
endpointsToEnrich.set(candidate.id, candidate.endpoints);
|
|
105
|
+
}
|
|
106
|
+
const windowInMs = this.routeTimeWindowMinutes * MINUTE_IN_MS;
|
|
107
|
+
const newEnrichments = await this.enrichmentRepository.getEnrichments({
|
|
108
|
+
segments: Array.from(endpointsToEnrich.values()),
|
|
109
|
+
windowStart: new Date(detectedAt.getTime() - windowInMs),
|
|
110
|
+
windowEnd: new Date(detectedAt.getTime() + windowInMs),
|
|
111
|
+
maxInfoPanelDistanceStops: this.maxInfoPanelDistanceStops,
|
|
112
|
+
});
|
|
113
|
+
const enrichments = new Map();
|
|
114
|
+
for (const [index, id] of Array.from(endpointsToEnrich.keys()).entries()) {
|
|
115
|
+
const enrichment = newEnrichments[index];
|
|
116
|
+
if (enrichment) {
|
|
117
|
+
enrichments.set(id, enrichment);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return enrichments;
|
|
121
|
+
}
|
|
122
|
+
parseAswStopId(stopId) {
|
|
123
|
+
const [nodeId, aswStopId] = GtfsStopParser.normalizedStopId(stopId).split("/").map(Number);
|
|
124
|
+
if (!this.isValidAswIdPart(nodeId) || !this.isValidAswIdPart(aswStopId)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return { nodeId, stopId: aswStopId };
|
|
128
|
+
}
|
|
129
|
+
isValidAswIdPart(value) {
|
|
130
|
+
return value !== undefined && Number.isInteger(value) && value >= 0 && value <= PG_INT_MAX;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Skipped segments are logged only at the debug level, as they repeat every refresh while the delay lasts
|
|
134
|
+
*/
|
|
135
|
+
skip(skipCounts, reason, segment) {
|
|
136
|
+
skipCounts.set(reason, (skipCounts.get(reason) ?? 0) + 1);
|
|
137
|
+
this.logger.debug(`${this.constructor.name}: skipping segment ${segment} (${reason}).`);
|
|
138
|
+
}
|
|
139
|
+
toDelayedSegment(candidate, enrichment) {
|
|
140
|
+
if (!enrichment.has_endpoint_stops) {
|
|
141
|
+
return SkipReason.EndpointStopsNotInGtfs;
|
|
142
|
+
}
|
|
143
|
+
if (!enrichment.from_stop || !enrichment.to_stop) {
|
|
144
|
+
return SkipReason.NoTripRunsWholeSegment;
|
|
145
|
+
}
|
|
146
|
+
if (enrichment.routes.length === 0) {
|
|
147
|
+
return SkipReason.NoAffectedRoutes;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
id: candidate.id,
|
|
151
|
+
external_from_stop_id: candidate.delayShape.from_stop_id,
|
|
152
|
+
external_to_stop_id: candidate.delayShape.to_stop_id,
|
|
153
|
+
from_stop_id: enrichment.from_stop.id,
|
|
154
|
+
from_stop_name: enrichment.from_stop.name,
|
|
155
|
+
to_stop_id: enrichment.to_stop.id,
|
|
156
|
+
to_stop_name: enrichment.to_stop.name,
|
|
157
|
+
expected_delay_seconds: candidate.delayShape.expected_delay,
|
|
158
|
+
stops: enrichment.stops,
|
|
159
|
+
routes: enrichment.routes,
|
|
160
|
+
info_panel_stops: enrichment.info_panel_stops,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
JISDelayedSegmentsDataService = __decorate([
|
|
165
|
+
injectable(),
|
|
166
|
+
__param(0, inject(JISContainerToken.JISDelayedSegmentEnrichmentRepository)),
|
|
167
|
+
__param(1, inject(JISContainerToken.JISDelayedSegmentsRepository)),
|
|
168
|
+
__param(2, inject(CoreToken.SimpleConfig)),
|
|
169
|
+
__param(3, inject(CoreToken.Logger)),
|
|
170
|
+
__metadata("design:paramtypes", [JISDelayedSegmentEnrichmentRepository,
|
|
171
|
+
JISDelayedSegmentsRepository, Object, Object])
|
|
172
|
+
], JISDelayedSegmentsDataService);
|
|
173
|
+
export { JISDelayedSegmentsDataService };
|
|
174
|
+
//# sourceMappingURL=JISDelayedSegmentsDataService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JISDelayedSegmentsDataService.js","sourceRoot":"","sources":["../../../../src/integration-engine/jis/services/JISDelayedSegmentsDataService.ts"],"names":[],"mappings":"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"}
|