115 lines
6.5 KiB
Go
115 lines
6.5 KiB
Go
|
package summarize
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/jackc/pgx/v4"
|
||
|
"hg.lukegb.com/lukegb/depot/go/trains/webapi"
|
||
|
)
|
||
|
|
||
|
type Querier interface {
|
||
|
Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
|
||
|
QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row
|
||
|
}
|
||
|
|
||
|
func Service(ctx context.Context, pc Querier, id int) (*webapi.ServiceData, error) {
|
||
|
row := pc.QueryRow(ctx, `
|
||
|
SELECT
|
||
|
ts.id, ts.rid, ts.uid, ts.rsid, ts.headcode, ts.scheduled_start_date::varchar,
|
||
|
ts.train_operator, COALESCE(rop.name, ''), COALESCE(rop.url, ''),
|
||
|
COALESCE(ts.delay_reason_code::int, 0), COALESCE(rlr.text, ''), COALESCE(ts.delay_reason_tiploc, ''), COALESCE(rlrloc.name, ''), COALESCE(rlrloc.crs, ''), COALESCE(rlrloc.toc, ''), COALESCE(rlrop.name, ''), COALESCE(rlrop.url, ''), COALESCE(ts.delay_reason_tiploc_near, false),
|
||
|
COALESCE(ts.cancellation_reason_code::int, 0), COALESCE(rlr.text, ''), COALESCE(ts.cancellation_reason_tiploc, ''), COALESCE(rlrloc.name, ''), COALESCE(rlrloc.crs, ''), COALESCE(rlrloc.toc, ''), COALESCE(rlrop.name, ''), COALESCE(rlrop.url, ''), COALESCE(ts.cancellation_reason_tiploc_near, false),
|
||
|
ts.active, ts.deleted, ts.cancelled
|
||
|
FROM
|
||
|
train_services ts
|
||
|
LEFT JOIN ref_tocs rop ON rop.toc=ts.train_operator
|
||
|
|
||
|
LEFT JOIN ref_late_running_reasons rlr ON rlr.code=ts.delay_reason_code::int
|
||
|
LEFT JOIN ref_locations rlrloc ON rlrloc.tiploc=ts.delay_reason_tiploc
|
||
|
LEFT JOIN ref_tocs rlrop ON rlrop.toc=rlrloc.toc
|
||
|
|
||
|
LEFT JOIN ref_cancel_reasons rc ON rc.code=ts.cancellation_reason_code::int
|
||
|
LEFT JOIN ref_locations rcloc ON rcloc.tiploc=ts.cancellation_reason_tiploc
|
||
|
LEFT JOIN ref_tocs rcop ON rcop.toc=rcloc.toc
|
||
|
WHERE
|
||
|
ts.id=$1
|
||
|
`, id)
|
||
|
|
||
|
sd := webapi.ServiceData{
|
||
|
DelayReason: &webapi.DisruptionReason{Location: &webapi.Location{Operator: &webapi.TrainOperator{}}},
|
||
|
CancelReason: &webapi.DisruptionReason{Location: &webapi.Location{Operator: &webapi.TrainOperator{}}},
|
||
|
Operator: &webapi.TrainOperator{},
|
||
|
}
|
||
|
if err := row.Scan(
|
||
|
&sd.Id, &sd.Rid, &sd.Uid, &sd.Rsid, &sd.Headcode, &sd.ScheduledStartDate,
|
||
|
&sd.Operator.Code, &sd.Operator.Name, &sd.Operator.Url,
|
||
|
&sd.DelayReason.Code, &sd.DelayReason.Text, &sd.DelayReason.Location.Tiploc, &sd.DelayReason.Location.Name, &sd.DelayReason.Location.Crs, &sd.DelayReason.Location.Operator.Code, &sd.DelayReason.Location.Operator.Name, &sd.DelayReason.Location.Operator.Url, &sd.DelayReason.NearLocation,
|
||
|
&sd.CancelReason.Code, &sd.CancelReason.Text, &sd.CancelReason.Location.Tiploc, &sd.CancelReason.Location.Name, &sd.CancelReason.Location.Crs, &sd.CancelReason.Location.Operator.Code, &sd.CancelReason.Location.Operator.Name, &sd.CancelReason.Location.Operator.Url, &sd.CancelReason.NearLocation,
|
||
|
&sd.IsActive, &sd.IsDeleted, &sd.IsCancelled,
|
||
|
); err != nil {
|
||
|
return nil, fmt.Errorf("reading from train_services for %d: %w", id, err)
|
||
|
}
|
||
|
sd.Canonicalize()
|
||
|
|
||
|
// Now for the locations.
|
||
|
rows, err := pc.Query(ctx, `
|
||
|
SELECT
|
||
|
tl.id,
|
||
|
tl.tiploc, COALESCE(rloc.name, ''), COALESCE(rloc.crs, ''), COALESCE(rloc.toc, ''), COALESCE(rloctoc.name, ''), COALESCE(rloctoc.url, ''),
|
||
|
tl.calling_point::varchar, COALESCE(tl.train_length, 0), tl.service_suppressed,
|
||
|
tl.schedule_cancelled,
|
||
|
COALESCE(tl.schedule_platform, ''), COALESCE(tl.platform, ''), COALESCE(tl.platform_confirmed, false), COALESCE(tl.platform_suppressed, false),
|
||
|
tl.schedule_false_destination_tiploc, COALESCE(rfdloc.name, ''), COALESCE(rfdloc.crs, ''), COALESCE(rfdloc.toc, ''), COALESCE(rfdloctoc.name, ''), COALESCE(rfdloctoc.url, ''),
|
||
|
|
||
|
tl.schedule_public_arrival, tl.schedule_working_arrival, tl.estimated_arrival, tl.working_estimated_arrival, tl.actual_arrival,
|
||
|
tl.schedule_public_departure, tl.schedule_working_departure, tl.estimated_departure, tl.working_estimated_departure, tl.actual_departure,
|
||
|
/* no public_pass */ tl.schedule_working_pass, tl.estimated_pass, tl.working_estimated_pass, tl.actual_pass
|
||
|
FROM
|
||
|
train_locations tl
|
||
|
LEFT JOIN ref_locations rloc ON rloc.tiploc=tl.tiploc
|
||
|
LEFT JOIN ref_tocs rloctoc ON rloctoc.toc=rloc.toc
|
||
|
|
||
|
LEFT JOIN ref_locations rfdloc ON rfdloc.tiploc=tl.schedule_false_destination_tiploc
|
||
|
LEFT JOIN ref_tocs rfdloctoc ON rfdloctoc.toc=rfdloc.toc
|
||
|
WHERE
|
||
|
tl.tsid=$1 AND tl.active_in_schedule
|
||
|
ORDER BY
|
||
|
COALESCE(tl.schedule_working_arrival, tl.schedule_working_pass, tl.schedule_working_departure)
|
||
|
`, id)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("querying locations for %d: %w", id, err)
|
||
|
}
|
||
|
defer rows.Close()
|
||
|
for rows.Next() {
|
||
|
loc := webapi.ServiceLocation{
|
||
|
Location: &webapi.Location{Operator: &webapi.TrainOperator{}},
|
||
|
Platform: &webapi.PlatformData{},
|
||
|
FalseDestination: &webapi.Location{Operator: &webapi.TrainOperator{}},
|
||
|
ArrivalTiming: &webapi.TimingData{PublicScheduled: &webapi.DateTime{}, WorkingScheduled: &webapi.DateTime{}, PublicEstimated: &webapi.DateTime{}, WorkingEstimated: &webapi.DateTime{}, Actual: &webapi.DateTime{}},
|
||
|
DepartureTiming: &webapi.TimingData{PublicScheduled: &webapi.DateTime{}, WorkingScheduled: &webapi.DateTime{}, PublicEstimated: &webapi.DateTime{}, WorkingEstimated: &webapi.DateTime{}, Actual: &webapi.DateTime{}},
|
||
|
PassTiming: &webapi.TimingData{PublicScheduled: &webapi.DateTime{}, WorkingScheduled: &webapi.DateTime{}, PublicEstimated: &webapi.DateTime{}, WorkingEstimated: &webapi.DateTime{}, Actual: &webapi.DateTime{}},
|
||
|
}
|
||
|
if err := rows.Scan(
|
||
|
&loc.Id,
|
||
|
&loc.Location.Tiploc, &loc.Location.Name, &loc.Location.Crs, &loc.Location.Operator.Code, &loc.Location.Operator.Name, &loc.Location.Operator.Url,
|
||
|
&loc.CallingPointType, &loc.TrainLength, &loc.ServiceSuppressed,
|
||
|
&loc.Cancelled,
|
||
|
&loc.Platform.Scheduled, &loc.Platform.Live, &loc.Platform.Confirmed, &loc.Platform.Suppressed,
|
||
|
&loc.FalseDestination.Tiploc, &loc.FalseDestination.Name, &loc.FalseDestination.Crs, &loc.FalseDestination.Operator.Code, &loc.FalseDestination.Operator.Name, &loc.FalseDestination.Operator.Url,
|
||
|
loc.ArrivalTiming.PublicScheduled, loc.ArrivalTiming.WorkingScheduled, loc.ArrivalTiming.PublicEstimated, loc.ArrivalTiming.WorkingEstimated, loc.ArrivalTiming.Actual,
|
||
|
loc.DepartureTiming.PublicScheduled, loc.DepartureTiming.WorkingScheduled, loc.DepartureTiming.PublicEstimated, loc.DepartureTiming.WorkingEstimated, loc.DepartureTiming.Actual,
|
||
|
/*loc.PassTiming.PublicScheduled,*/ loc.PassTiming.WorkingScheduled, loc.PassTiming.PublicEstimated, loc.PassTiming.WorkingEstimated, loc.PassTiming.Actual,
|
||
|
); err != nil {
|
||
|
return nil, fmt.Errorf("scanning locations for %d: %w", id, err)
|
||
|
}
|
||
|
loc.Canonicalize()
|
||
|
sd.Locations = append(sd.Locations, &loc)
|
||
|
}
|
||
|
if rows.Err() != nil {
|
||
|
return nil, fmt.Errorf("iterating over locations for %d: %w", id, err)
|
||
|
}
|
||
|
|
||
|
return &sd, nil
|
||
|
}
|