depot/go/trains/darwin/darwindb/ddbschedule.go

112 lines
4.2 KiB
Go

package darwindb
import (
"context"
"encoding/json"
"fmt"
"log"
pgx "github.com/jackc/pgx/v4"
"git.lukegb.com/lukegb/depot/go/trains/darwin"
)
type scheduleMode int
const (
scheduleModeOverwrite scheduleMode = iota
scheduleModeIgnoreExisting
)
// handleSchedule processes a Darwin Schedule.
func handleSchedule(ctx context.Context, tx pgx.Tx, s *darwin.Schedule, a *Affected, sm scheduleMode) error {
startTS, err := startTime(s)
if err != nil {
return fmt.Errorf("finding start time for RID %v: %w", s.RID, err)
}
var cancelCode, cancelTIPLOC *string
var cancelTIPLOCNear *bool
if s.CancellationReason != nil {
cancelCode = &s.CancellationReason.Code
cancelTIPLOC = &s.CancellationReason.TIPLOC
cancelTIPLOCNear = &s.CancellationReason.Near
}
sql := `
INSERT INTO train_services
(rid, uid, rsid, headcode, scheduled_start_date, effective_start_time,
train_operator, status, train_category, is_passenger_svc, is_charter, is_q_train,
cancellation_reason_code, cancellation_reason_tiploc, cancellation_reason_tiploc_near,
active, deleted, cancelled)
VALUES
($1, $2, $3, $4, $5, $6,
$7, COALESCE($8, 'P'), COALESCE($9, 'OO'), COALESCE($10, true), COALESCE($11, false), COALESCE($12, false),
$13, $14, $15,
COALESCE($16, true), $17, COALESCE($18, false))
ON CONFLICT (rid)`
switch sm {
case scheduleModeOverwrite:
sql += ` DO UPDATE SET
uid=$2, rsid=$3, headcode=$4, scheduled_start_date=$5, effective_start_time=$6,
train_operator=$7, status=COALESCE($8, 'P'), train_category=COALESCE($9, 'OO'), is_passenger_svc=COALESCE($10, true), is_charter=COALESCE($11, false), is_q_train=COALESCE($12, train_services.is_q_train),
cancellation_reason_code=$13, cancellation_reason_tiploc=$14, cancellation_reason_tiploc_near=$15,
active=COALESCE($16, 'true'), deleted=$17, cancelled=COALESCE($18, 'false')
`
case scheduleModeIgnoreExisting:
sql += ` DO NOTHING
`
}
sql += `RETURNING id`
row := tx.QueryRow(ctx, sql,
s.RID, s.UID, s.RSID, s.TrainID, s.SSD, startTS,
s.TOC, s.Status, s.TrainCat, s.IsPassengerSvc, s.IsCharter, s.IsQTrain,
cancelCode, cancelTIPLOC, cancelTIPLOCNear,
s.IsActive, s.Deleted, s.Cancelled)
var serviceID int
if err := row.Scan(&serviceID); err == pgx.ErrNoRows && sm == scheduleModeIgnoreExisting {
// The row existed already.
return nil
} else if err != nil {
log.Printf("inserting schedule for RID %v: %v", s.RID, err)
return fmt.Errorf("inserting schedule for RID %v: %w", s.RID, err)
}
a.RID(s.RID)
a.TSID(serviceID)
// Insert/update all the locations.
var activeLocations []int
for _, cp := range s.CallingPoints {
var locid int
row := tx.QueryRow(ctx, `
SELECT train_locations_upsert_from_schedule(
p_tsid => $1, p_rid => $2, p_tiploc => $3, p_calling_point => $4,
p_schedule_public_arrival => $5, p_schedule_working_arrival => $6,
p_schedule_public_departure => $7, p_schedule_working_departure => $8,
p_schedule_working_pass => $9,
p_schedule_route_delay => $10, p_schedule_activity => $11, p_schedule_planned_activity => $12, p_schedule_cancelled => $13,
p_schedule_false_destination_tiploc => $14, p_schedule_platform => $15)
`,
serviceID, s.RID, cp.TIPLOC, cp.XMLName.Local,
timeToTimestamp(cp.PTA, startTS), timeToTimestamp(cp.WTA, startTS),
timeToTimestamp(cp.PTD, startTS), timeToTimestamp(cp.WTD, startTS),
timeToTimestamp(cp.WTP, startTS),
cp.RDelay, cp.Act, cp.PlanAct, cp.Cancelled,
cp.FD, cp.Platform,
)
if err := row.Scan(&locid); err != nil {
log.Printf("updating location %v from RID %v/my ID %v: %v", cp.TIPLOC, s.RID, serviceID, err)
zz, _ := json.MarshalIndent(s, "", " ")
log.Println(string(zz))
zz, _ = json.MarshalIndent(cp, "", " ")
log.Println(string(zz))
return fmt.Errorf("updating location %v from RID %v/my ID %v: %w", cp.TIPLOC, s.RID, serviceID, err)
} else {
activeLocations = append(activeLocations, locid)
}
a.TIPLOC(cp.TIPLOC)
}
if _, err := tx.Exec(ctx, "UPDATE train_locations SET active_in_schedule=false WHERE tsid=$1 AND NOT (id = ANY ($2))", serviceID, activeLocations); err != nil {
return fmt.Errorf("marking old locations as removed from schedule for RID %v/my ID %v (active locations: %v): %w", s.RID, serviceID, activeLocations, err)
}
fmt.Printf("s")
return nil
}