Added kmb schedules
This commit is contained in:
parent
b251e35be4
commit
41be1db381
@ -2,18 +2,52 @@ package kmb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
query "github.com/tgckpg/golifehk/query"
|
||||
utils "github.com/tgckpg/golifehk/utils"
|
||||
)
|
||||
|
||||
type QueryResult struct {
|
||||
Schedules *map[*RouteStop] *[] *Schedule
|
||||
Lang string
|
||||
Error error
|
||||
Query *query.QueryObject
|
||||
}
|
||||
|
||||
func writeRouteHead( sb *strings.Builder, r *RouteStop ) {
|
||||
utils.WriteMDv2Text( sb, r.RouteId )
|
||||
if r.Direction == "O" {
|
||||
sb.WriteString( "↑" )
|
||||
} else if r.Direction == "I" {
|
||||
sb.WriteString( "↓" )
|
||||
}
|
||||
if r.ServiceType != "1" {
|
||||
utils.WriteMDv2Text( sb, utils.ToPower( r.ServiceType ) )
|
||||
}
|
||||
}
|
||||
|
||||
func writeShortRoute( lang *string, sb *strings.Builder, r *RouteStop ) {
|
||||
|
||||
if r.PrevStop() != nil {
|
||||
utils.WriteMDv2Text( sb, (*(r.PrevStop().BusStop).Name)[ *lang ] )
|
||||
sb.WriteString( " \\> " )
|
||||
}
|
||||
|
||||
sb.WriteString( "*" )
|
||||
utils.WriteMDv2Text( sb, (*(r.BusStop).Name)[ *lang ] )
|
||||
sb.WriteString( "*" )
|
||||
|
||||
if r.NextStop() != nil {
|
||||
sb.WriteString( " \\> " )
|
||||
utils.WriteMDv2Text( sb, (*(r.NextStop().BusStop).Name)[ *lang ] )
|
||||
}
|
||||
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
|
||||
func ( this *QueryResult ) Message() ( string, error ) {
|
||||
|
||||
if this.Error != nil {
|
||||
@ -23,35 +57,119 @@ func ( this *QueryResult ) Message() ( string, error ) {
|
||||
sb := strings.Builder{}
|
||||
|
||||
if 0 < len( *this.Query.Results ) {
|
||||
// Print Stop Name, the print the list of routes
|
||||
if this.Query.Key == "" {
|
||||
for _, item := range *this.Query.Results {
|
||||
var b *BusStop
|
||||
b = any( item ).( *BusStop )
|
||||
|
||||
// Print Stop Names, then print the list of routes
|
||||
if this.Query.Key == "" {
|
||||
busStops := map[string] *BusStop{}
|
||||
for _, item := range *this.Query.Results {
|
||||
var r *RouteStop
|
||||
r = any( item ).( *RouteStop )
|
||||
|
||||
b := r.BusStop
|
||||
if b.Routes == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
busStops[ b.BusStopId ] = b
|
||||
}
|
||||
|
||||
for _, b := range busStops {
|
||||
utils.WriteMDv2Text( &sb, (*b.Name)[ this.Lang ] )
|
||||
sb.WriteString( "\n " )
|
||||
for _, route := range *b.Routes {
|
||||
utils.WriteMDv2Text( &sb, route.RouteId )
|
||||
if route.Direction == "O" {
|
||||
sb.WriteString( "↑" )
|
||||
} else if route.Direction == "I" {
|
||||
sb.WriteString( "↓" )
|
||||
}
|
||||
if route.ServiceType != "1" {
|
||||
utils.WriteMDv2Text( &sb, utils.ToPower( route.ServiceType ) )
|
||||
}
|
||||
writeRouteHead( &sb, route )
|
||||
sb.WriteString( " " )
|
||||
}
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
|
||||
// We got a route key
|
||||
} else {
|
||||
|
||||
// We also got other search keys with 1 < Results
|
||||
// Get the ETA for this stop
|
||||
if 1 < len( *this.Query.SearchTerms ) {
|
||||
|
||||
now := time.Now()
|
||||
for _, item := range *this.Query.Results {
|
||||
var r *RouteStop
|
||||
r = any( item ).( *RouteStop )
|
||||
writeRouteHead( &sb, r )
|
||||
sb.WriteString( "\n" )
|
||||
writeShortRoute( &this.Lang, &sb, r )
|
||||
|
||||
for _, schedule := range *(*this.Schedules)[ r ] {
|
||||
|
||||
if !schedule.ETA.IsZero() {
|
||||
|
||||
_m := schedule.ETA.Sub( now ).Minutes()
|
||||
|
||||
sb.WriteString( " \\* " )
|
||||
txt := "%.0f min(s)"
|
||||
|
||||
if this.Lang == "zh-Hant" {
|
||||
txt = "%.0f 分鐘"
|
||||
}
|
||||
|
||||
utils.WriteMDv2Text( &sb, fmt.Sprintf( txt, _m ) )
|
||||
|
||||
if _m < 0 {
|
||||
sb.WriteString( " 走左了?" )
|
||||
}
|
||||
}
|
||||
|
||||
if schedule.Remarks_en != "" {
|
||||
sb.WriteString( " \\*\\* " )
|
||||
switch this.Lang {
|
||||
case "en":
|
||||
utils.WriteMDv2Text( &sb, schedule.Remarks_en )
|
||||
case "zh-Hant":
|
||||
utils.WriteMDv2Text( &sb, schedule.Remarks_tc )
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
|
||||
// We got only the route key, proceed to list the route stops
|
||||
} else {
|
||||
// Result contains all route stops, we only need the starting one
|
||||
routes := [] *RouteStop{}
|
||||
|
||||
for _, item := range *this.Query.Results {
|
||||
var r *RouteStop
|
||||
r = any( item ).( *RouteStop )
|
||||
if r.PrevStop() == nil {
|
||||
routes = append( routes, r )
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort( ByRoute( routes ) )
|
||||
|
||||
for _, r := range routes {
|
||||
writeRouteHead( &sb, r )
|
||||
sb.WriteString( "\n" )
|
||||
for {
|
||||
b := *r.BusStop
|
||||
utils.WriteMDv2Text( &sb, (*b.Name)[ this.Lang ] )
|
||||
r = r.NextStop()
|
||||
if r == nil {
|
||||
break
|
||||
}
|
||||
|
||||
sb.WriteString( " \\> " )
|
||||
}
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
fmt.Print( this.Query.Key )
|
||||
} else {
|
||||
return "", fmt.Errorf( "No Results" )
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
|
@ -37,8 +37,29 @@ func ( routeStop RouteStop ) NextStop() *RouteStop {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ByRouteId []RouteStop
|
||||
func ( this *RouteStop ) Reload() {
|
||||
|
||||
func (a ByRouteId) Len() int { return len(a) }
|
||||
func (a ByRouteId) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByRouteId) Less(i, j int) bool { return a[i].RouteId < a[j].RouteId }
|
||||
searchData := [] *string{}
|
||||
busStop := *this.BusStop
|
||||
searchData = append( searchData, &busStop.Name_en )
|
||||
searchData = append( searchData, &busStop.Name_tc )
|
||||
|
||||
this.Key = &this.RouteId
|
||||
this.SearchData = &searchData
|
||||
}
|
||||
|
||||
type ByRoute [] *RouteStop
|
||||
|
||||
func (a ByRoute) Len() int { return len(a) }
|
||||
func (a ByRoute) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByRoute) Less(i, j int) bool {
|
||||
_a := *a[i]
|
||||
_b := *a[j]
|
||||
if _a.RouteId == _b.RouteId {
|
||||
if _a.Direction == _b.Direction {
|
||||
return _a.ServiceType < _b.ServiceType
|
||||
}
|
||||
return _a.Direction < _b.Direction
|
||||
}
|
||||
return _a.RouteId < _b.RouteId
|
||||
}
|
||||
|
@ -11,13 +11,13 @@ func Query( lang string, message string ) query.IQueryResult {
|
||||
var err error
|
||||
|
||||
qr := QueryResult{ Lang: lang }
|
||||
busStops, err := getBusStops()
|
||||
routeStops, err := getRouteStops()
|
||||
if err != nil {
|
||||
qr.Error = err
|
||||
goto qrReturn
|
||||
}
|
||||
|
||||
qo, err = query.Parse( strings.ToUpper( message ), busStops )
|
||||
qo, err = query.Parse( strings.ToUpper( message ), routeStops )
|
||||
if err != nil {
|
||||
qr.Error = err
|
||||
goto qrReturn
|
||||
@ -25,6 +25,23 @@ func Query( lang string, message string ) query.IQueryResult {
|
||||
|
||||
qr.Query = qo
|
||||
|
||||
if 0 < len( *qo.Results ) && 1 < len( *qo.SearchTerms ) {
|
||||
|
||||
rSchedules := map[*RouteStop] *[] *Schedule{}
|
||||
for _, item := range *qo.Results {
|
||||
var r *RouteStop
|
||||
r = any( item ).( *RouteStop )
|
||||
schedules, err := getSchedule( r )
|
||||
if err != nil {
|
||||
qr.Error = err
|
||||
break
|
||||
}
|
||||
|
||||
rSchedules[r] = schedules
|
||||
}
|
||||
qr.Schedules = &rSchedules
|
||||
}
|
||||
|
||||
qrReturn:
|
||||
var iqr query.IQueryResult
|
||||
iqr = &qr
|
||||
|
@ -18,4 +18,19 @@ func TestQuerySchedule( t *testing.T ) {
|
||||
if err == nil {
|
||||
t.Errorf( "Expected Error: %s, got \"\" instead", mesg )
|
||||
}
|
||||
|
||||
qo = Query( "zh-Hant", "大欖" )
|
||||
mesg, err = qo.Message()
|
||||
if err != nil {
|
||||
t.Errorf( "Unexpected Error: %s", err )
|
||||
}
|
||||
|
||||
fmt.Println( mesg )
|
||||
|
||||
qo = Query( "zh-Hant", "261B 大欖" )
|
||||
mesg, err = qo.Message()
|
||||
if err != nil {
|
||||
t.Errorf( "Unexpected Error: %s", err )
|
||||
}
|
||||
fmt.Println( mesg )
|
||||
}
|
||||
|
@ -16,16 +16,18 @@ import (
|
||||
var JSON_ROUTESTOPS string = filepath.Join( utils.WORKDIR, "kmb-routestops.json" )
|
||||
var JSON_BUSSTOPS string = filepath.Join( utils.WORKDIR, "kmb-busstops.json" )
|
||||
|
||||
func readRouteStopsData( busStops *map[string] *BusStop, buff *bytes.Buffer ) error {
|
||||
func readRouteStopsData( busStops *map[string] *BusStop, buff *bytes.Buffer ) ( *[] *RouteStop, error ) {
|
||||
|
||||
routeStopsData := RouteStops{}
|
||||
err := json.Unmarshal( buff.Bytes(), &routeStopsData )
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// routeStops[ Route ][ ServiceType ][ Direction ][ Seq ] = RouteStop
|
||||
routeStops := map[string] *map[string] *map[ string ] *map[ int ] *RouteStop{}
|
||||
allRouteStops := [] *RouteStop{}
|
||||
|
||||
for _, entry := range routeStopsData.RouteStops {
|
||||
|
||||
busStop := (*busStops)[ entry.StationId ]
|
||||
@ -63,16 +65,17 @@ func readRouteStopsData( busStops *map[string] *BusStop, buff *bytes.Buffer ) er
|
||||
if direction == nil {
|
||||
direction = &map[ int ] *RouteStop{}
|
||||
(*service)[ entry.Direction ] = direction
|
||||
entry.RouteStops = direction
|
||||
}
|
||||
entry.RouteStops = direction
|
||||
|
||||
seq := (*direction)[ entry.StationSeq ]
|
||||
if seq == nil {
|
||||
(*direction)[ entry.StationSeq ] = entry
|
||||
}
|
||||
allRouteStops = append( allRouteStops, entry )
|
||||
}
|
||||
|
||||
return nil
|
||||
return &allRouteStops, nil
|
||||
}
|
||||
|
||||
func readBusStopsData( buff *bytes.Buffer ) ( *map[string]*BusStop, error ) {
|
||||
@ -96,7 +99,7 @@ func readBusStopsData( buff *bytes.Buffer ) ( *map[string]*BusStop, error ) {
|
||||
return &busStopMap, nil
|
||||
}
|
||||
|
||||
func getBusStops() (*[] query.ISearchable, error) {
|
||||
func getRouteStops() (*[] query.ISearchable, error) {
|
||||
|
||||
QUERY_FUNC := func() ( io.ReadCloser, error ) {
|
||||
resp, err := http.Get( "https://data.etabus.gov.hk/v1/transport/kmb/stop" )
|
||||
@ -129,14 +132,15 @@ func getBusStops() (*[] query.ISearchable, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = readRouteStopsData( busStopMap, buff )
|
||||
routeStops, err := readRouteStopsData( busStopMap, buff )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchables := [] query.ISearchable{}
|
||||
for _, busStop := range *busStopMap {
|
||||
searchables = append( searchables, busStop )
|
||||
for _, routeStop := range *routeStops {
|
||||
searchables = append( searchables, routeStop )
|
||||
routeStop.Reload()
|
||||
}
|
||||
|
||||
return &searchables, nil
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func TestRouteStops(t *testing.T) {
|
||||
_, err := getBusStops()
|
||||
_, err := getRouteStops()
|
||||
if err != nil {
|
||||
t.Error( err )
|
||||
}
|
||||
|
62
datasources/kmb/schedules.go
Normal file
62
datasources/kmb/schedules.go
Normal file
@ -0,0 +1,62 @@
|
||||
package kmb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/tgckpg/golifehk/utils"
|
||||
)
|
||||
|
||||
type Schedule struct {
|
||||
ETA time.Time `json:"eta,string"`
|
||||
Index int `json:"eta_seq"`
|
||||
DateCreated time.Time `json:"data_timestamp,string"`
|
||||
Remarks_en string `json:"rmk_en"`
|
||||
Remarks_sc string `json:"rmk_sc"`
|
||||
Remarks_tc string `json:"rmk_tc"`
|
||||
}
|
||||
|
||||
type Schedules struct {
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
DateCreated time.Time `json:"generated_timestamp,string"`
|
||||
Schedules [] *Schedule `json:"data"`
|
||||
}
|
||||
|
||||
func getSchedule( r *RouteStop ) ( *[] *Schedule, error ) {
|
||||
|
||||
CACHE_PATH := filepath.Join(
|
||||
utils.WORKDIR,
|
||||
"kmb-" + r.BusStop.BusStopId + "-" + r.RouteId + "-" + r.ServiceType + ".json",
|
||||
)
|
||||
|
||||
QUERY_FUNC := func() ( io.ReadCloser, error ) {
|
||||
resp, err := http.Get( fmt.Sprintf(
|
||||
"https://data.etabus.gov.hk/v1/transport/kmb/eta/%s/%s/%s",
|
||||
r.BusStop.BusStopId,
|
||||
r.RouteId,
|
||||
r.ServiceType,
|
||||
) )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
buff, err := utils.CacheStream( CACHE_PATH, QUERY_FUNC, 60 )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schedules := Schedules{}
|
||||
err = json.Unmarshal( buff.Bytes(), &schedules )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &schedules.Schedules, nil
|
||||
}
|
@ -18,17 +18,17 @@ type QueryResult struct {
|
||||
|
||||
func writeShortRoute( lang *string, sb *strings.Builder, b *BusStop ) {
|
||||
if b.PrevStop() != nil {
|
||||
utils.WriteMDv2Text( sb, (*b.PrevStop().Name)[ *lang ] )
|
||||
utils.WriteMDv2Text( sb, (*b.PrevStop().Name)[ *lang ] )
|
||||
sb.WriteString( " \\> " )
|
||||
}
|
||||
|
||||
sb.WriteString( "*" )
|
||||
utils.WriteMDv2Text( sb, (*b.Name)[ *lang ] )
|
||||
utils.WriteMDv2Text( sb, (*b.Name)[ *lang ] )
|
||||
sb.WriteString( "*" )
|
||||
|
||||
if b.NextStop() != nil {
|
||||
sb.WriteString( " \\> " )
|
||||
utils.WriteMDv2Text( sb, (*b.NextStop().Name)[ *lang ] )
|
||||
utils.WriteMDv2Text( sb, (*b.NextStop().Name)[ *lang ] )
|
||||
}
|
||||
|
||||
sb.WriteString( "\n" )
|
||||
@ -49,7 +49,7 @@ func ( this QueryResult ) Message() ( string, error ) {
|
||||
sort.Sort( query.ByKey( *q.Results ) )
|
||||
for _, entry := range *q.Results {
|
||||
busStop := any( entry ).( *BusStop )
|
||||
utils.WriteMDv2Text( &sb, busStop.RouteId )
|
||||
utils.WriteMDv2Text( &sb, busStop.RouteId )
|
||||
sb.WriteString( " " )
|
||||
writeShortRoute( &this.Lang, &sb, busStop )
|
||||
}
|
||||
@ -79,12 +79,18 @@ func ( this QueryResult ) Message() ( string, error ) {
|
||||
for _, d := range keys {
|
||||
b := st[ d ]
|
||||
sb.WriteString( q.Key )
|
||||
sb.WriteString( "\\-" )
|
||||
utils.WriteMDv2Text( &sb, d )
|
||||
|
||||
if d == "O" {
|
||||
sb.WriteString( "↑" )
|
||||
} else if d == "I" {
|
||||
sb.WriteString( "↓" )
|
||||
} else {
|
||||
sb.WriteString( "\\?" )
|
||||
}
|
||||
sb.WriteString( "\n " )
|
||||
|
||||
for {
|
||||
utils.WriteMDv2Text( &sb, (*b.Name)[ this.Lang ] )
|
||||
utils.WriteMDv2Text( &sb, (*b.Name)[ this.Lang ] )
|
||||
b = b.NextStop()
|
||||
if b == nil {
|
||||
break
|
||||
@ -111,16 +117,16 @@ func ( this QueryResult ) Message() ( string, error ) {
|
||||
for _, bus := range buses.Buses {
|
||||
sb.WriteString( " \\* " )
|
||||
if bus.ETAText == "" {
|
||||
utils.WriteMDv2Text( &sb, bus.ETDText )
|
||||
utils.WriteMDv2Text( &sb, bus.ETDText )
|
||||
} else {
|
||||
utils.WriteMDv2Text( &sb, bus.ETAText )
|
||||
utils.WriteMDv2Text( &sb, bus.ETAText )
|
||||
}
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
sb.WriteString( "\n" )
|
||||
}
|
||||
} else {
|
||||
utils.WriteMDv2Text( &sb, "Schedules are empty...perhaps Out of Service Time?" )
|
||||
utils.WriteMDv2Text( &sb, "Schedules are empty...perhaps Out of Service Time?" )
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user