Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backend/src/gtfs/dto/round-trip.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';

const RoundTripQuerySchema = z.object({
orgStopId: z.string().min(1, 'Origin stop ID is required'),
destStopId: z.string().min(1, 'Destination stop ID is required'),
date: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in YYYY-MM-DD format'),
});
Comment on lines +4 to +10
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should block origin and destination from being the same stop. Zod can do this with a refine/superRefine. We could also add a safer date parse/validation so we don’t accept a bad date that later becomes Invalid Date.


export class RoundTripQueryDto extends createZodDto(RoundTripQuerySchema) {}
70 changes: 70 additions & 0 deletions backend/src/gtfs/gtfs.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Controller, Get, Query } from '@nestjs/common';
import { InjectRepository } from '@mikro-orm/nestjs';
import { EntityRepository } from '@mikro-orm/postgresql';
import { GTFSStopTime } from '../entities';
import { RoundTripQueryDto } from './dto/round-trip.dto';
import { ApiTags, ApiOkResponse } from '@nestjs/swagger';

@Controller('round_trip')
@ApiTags('GTFS - Round Trip')
export class RoundTripController {
constructor(
@InjectRepository(GTFSStopTime)
private readonly stopTimeRepository: EntityRepository<GTFSStopTime>,
) {}

@Get()
@ApiOkResponse({
description: 'Returns stop times for origin and destination stops',
})
async getRoundTrip(@Query() query: RoundTripQueryDto) {
const targetDate = new Date(query.date);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong, but it seems like the logic here would be just getting any time of any bus/train that is stopped at the specified stop, regardless of direction and where it's headed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing the YYYY-MM-DD string with new Date() can shift the day because of timezones. Can we parse it as a plain date (e.g., split year/month/day or use a UTC parse) so it always matches the intended service date?


const orgStopTimes = await this.stopTimeRepository.find(
{
stop: { stopid: query.orgStopId },
trip: {
calendarDate: {
date: targetDate,
},
},
},
{
populate: ['trip', 'stop'],
orderBy: { departureTime: 'ASC' },
},
);

const destStopTimes = await this.stopTimeRepository.find(
{
stop: { stopid: query.destStopId },
trip: {
calendarDate: {
date: targetDate,
},
},
},
{
populate: ['trip', 'stop'],
orderBy: { departureTime: 'ASC' },
},
);
Comment on lines +23 to +51
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now we fetch origin and destination stop times separately, so the client could accidentally pair different trips or the wrong direction. Could we query trips that contain both stops, ensure the origin stop_sequence is before the destination’s, and (if available) match direction/headsign? Also, we should skip trips canceled for that date (exceptionType=2).


return {
orgStopTimes: orgStopTimes.map((st) => ({
id: st.id,
trip_id: st.trip.trip_id,
arrival_time: st.arrivalTime as unknown as string,
departure_time: st.departureTime as unknown as string,
stop_id: st.stop.stopid,
})),
destStopTimes: destStopTimes.map((st) => ({
id: st.id,
trip_id: st.trip.trip_id,
arrival_time: st.arrivalTime as unknown as string,
departure_time: st.departureTime as unknown as string,
stop_id: st.stop.stopid,
})),
};
Comment on lines +53 to +68
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We return two arrays and the client has to match them up. Could we instead return a list of matched trips, each with its origin/destination times, sorted by origin departure? That would make the endpoint easier to use.

}
}
2 changes: 2 additions & 0 deletions backend/src/gtfs/gtfs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { MikroOrmModule } from '@mikro-orm/nestjs';

import { GtfsService } from './gtfs.service';
import { RoundTripController } from './gtfs.controller';
import {
Agency,
GTFSRoute,
Expand All @@ -24,6 +25,7 @@ import { ConfigModule } from '@nestjs/config';
]),
ConfigModule,
],
controllers: [RoundTripController],
providers: [GtfsService],
exports: [GtfsService],
})
Expand Down
Loading