132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
|
||
|
pgx "github.com/jackc/pgx/v4"
|
||
|
)
|
||
|
|
||
|
func fetchWork(ctx context.Context, pc *pgx.Conn) ([]string, error) {
|
||
|
rows, err := pc.Query(ctx, `SELECT tiploc FROM ref_locations WHERE name=tiploc AND override_name IS NULL`)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("querying for work: %w", err)
|
||
|
}
|
||
|
defer rows.Close()
|
||
|
|
||
|
var tiplocs []string
|
||
|
for rows.Next() {
|
||
|
var tiploc string
|
||
|
if err := rows.Scan(&tiploc); err != nil {
|
||
|
return nil, fmt.Errorf("scanning row: %w", err)
|
||
|
}
|
||
|
tiplocs = append(tiplocs, tiploc)
|
||
|
}
|
||
|
if rows.Err() != nil {
|
||
|
return nil, fmt.Errorf("retrieving rows for work: %w", err)
|
||
|
}
|
||
|
|
||
|
return tiplocs, nil
|
||
|
}
|
||
|
|
||
|
type RTT struct {
|
||
|
Username, Password string
|
||
|
}
|
||
|
|
||
|
type RTTLocationDetail struct {
|
||
|
TIPLOC string `json:"tiploc"`
|
||
|
CRS string `json:"crs"`
|
||
|
Name string `json:"name"`
|
||
|
Description string `json:"description"`
|
||
|
}
|
||
|
|
||
|
type RTTLocationContainer struct {
|
||
|
Location *RTTLocationDetail `json:"location"`
|
||
|
}
|
||
|
|
||
|
func (r RTT) LocationDetail(ctx context.Context, inp string) (*RTTLocationDetail, error) {
|
||
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.rtt.io/api/v1/json/search/%v", inp), nil)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("formulating query for %q: %w", inp, err)
|
||
|
}
|
||
|
req.SetBasicAuth(r.Username, r.Password)
|
||
|
|
||
|
resp, err := http.DefaultClient.Do(req)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("querying API for %q: %w", inp, err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
if resp.StatusCode != http.StatusOK {
|
||
|
return nil, fmt.Errorf("status for %q was %d %v; wanted 200 OK", inp, resp.StatusCode, resp.Status)
|
||
|
}
|
||
|
|
||
|
var cont RTTLocationContainer
|
||
|
if err := json.NewDecoder(resp.Body).Decode(&cont); err != nil {
|
||
|
return nil, fmt.Errorf("unmarshalling API response for %q: %w", inp, err)
|
||
|
}
|
||
|
|
||
|
return cont.Location, nil
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
pc, err := pgx.Connect(ctx, "host=/var/run/postgresql database=trains search_path=darwindb")
|
||
|
if err != nil {
|
||
|
log.Fatalf("pgx.Connect: %v", err)
|
||
|
}
|
||
|
defer pc.Close(context.Background())
|
||
|
|
||
|
tiplocs, err := fetchWork(ctx, pc)
|
||
|
if err != nil {
|
||
|
log.Fatalf("fetchWork: %v", err)
|
||
|
}
|
||
|
log.Printf("%d tiplocs to process", len(tiplocs))
|
||
|
|
||
|
r := RTT{Username: "rttapi_lukegb", Password: "5c1e5ae8a88882b090cd2e55225a3f0f581ec57c"}
|
||
|
|
||
|
var rttApiErrCount int
|
||
|
for n, t := range tiplocs {
|
||
|
if rttApiErrCount == 20 {
|
||
|
log.Println("Too many errors from RTT, stopping")
|
||
|
break
|
||
|
}
|
||
|
if n%20 == 0 {
|
||
|
log.Printf("%d/%d (%d%%)", n, len(tiplocs), (n*100)/len(tiplocs))
|
||
|
}
|
||
|
|
||
|
d, err := r.LocationDetail(ctx, t)
|
||
|
if err != nil {
|
||
|
log.Printf("LocationDetail(ctx, %q): %v", t, err)
|
||
|
rttApiErrCount++
|
||
|
continue
|
||
|
}
|
||
|
if d == nil {
|
||
|
log.Printf("got a nil location for %q!", t)
|
||
|
rttApiErrCount++
|
||
|
continue
|
||
|
}
|
||
|
rttApiErrCount = 0 // reset the breaker
|
||
|
if d.Name == "" {
|
||
|
log.Printf("No name for %q", t)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
ct, err := pc.Exec(ctx, `UPDATE ref_locations SET name=$2, override_name=$2, override_name_src='RTT' WHERE tiploc=$1 AND override_name IS NULL AND name=tiploc`, t, d.Name)
|
||
|
if err != nil {
|
||
|
log.Printf("Updating database for %q failed: %w", t, err)
|
||
|
break
|
||
|
}
|
||
|
if ra := ct.RowsAffected(); ra != 1 {
|
||
|
log.Printf("Updated %d rows for %q; wanted 1???", ra, t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log.Println("terminating...")
|
||
|
}
|