Properly handle REFERENCE_ID for future changes
This commit is contained in:
@@ -18,6 +18,9 @@ type BusStop struct {
|
|||||||
// RouteStops[ StationSeq ] = BusStop
|
// RouteStops[ StationSeq ] = BusStop
|
||||||
RouteStops *map[int]*BusStop
|
RouteStops *map[int]*BusStop
|
||||||
|
|
||||||
|
// AltRoutes
|
||||||
|
AltRoutes *map[string]*BusStop
|
||||||
|
|
||||||
i18n.Generics
|
i18n.Generics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,148 +1,206 @@
|
|||||||
package bus
|
package bus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
query "github.com/tgckpg/golifehk/query"
|
query "github.com/tgckpg/golifehk/query"
|
||||||
utils "github.com/tgckpg/golifehk/utils"
|
utils "github.com/tgckpg/golifehk/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QueryResult struct {
|
type QueryResult struct {
|
||||||
Schedules *map[*BusStop] *BusStopBuses
|
Schedules *map[*BusStop]*BusStopBuses
|
||||||
Lang string
|
Lang string
|
||||||
Error error
|
Error error
|
||||||
Query *query.QueryObject
|
Query *query.QueryObject
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeShortRoute( lang *string, sb *strings.Builder, b *BusStop ) {
|
func writeShortRoute(lang *string, sb *strings.Builder, b *BusStop) {
|
||||||
if b.PrevStop() != nil {
|
if b.PrevStop() != nil {
|
||||||
utils.WriteMDv2Text( sb, (*b.PrevStop().Name)[ *lang ] )
|
utils.WriteMDv2Text(sb, (*b.PrevStop().Name)[*lang])
|
||||||
sb.WriteString( " \\> " )
|
sb.WriteString(" \\> ")
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteString( "*" )
|
sb.WriteString("*")
|
||||||
utils.WriteMDv2Text( sb, (*b.Name)[ *lang ] )
|
utils.WriteMDv2Text(sb, (*b.Name)[*lang])
|
||||||
sb.WriteString( "*" )
|
sb.WriteString("*")
|
||||||
|
|
||||||
if b.NextStop() != nil {
|
if b.NextStop() != nil {
|
||||||
sb.WriteString( " \\> " )
|
sb.WriteString(" \\> ")
|
||||||
utils.WriteMDv2Text( sb, (*b.NextStop().Name)[ *lang ] )
|
utils.WriteMDv2Text(sb, (*b.NextStop().Name)[*lang])
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteString( "\n" )
|
sb.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ( this QueryResult ) Message() ( string, error ) {
|
func (this QueryResult) Message() (string, error) {
|
||||||
|
|
||||||
if this.Error != nil {
|
if this.Error != nil {
|
||||||
return "", this.Error
|
return "", this.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
sb := strings.Builder{}
|
sb := strings.Builder{}
|
||||||
|
|
||||||
if this.Schedules == nil {
|
if this.Schedules == nil {
|
||||||
|
|
||||||
q := *this.Query
|
q := *this.Query
|
||||||
|
|
||||||
if len( *q.Results ) == 0 {
|
if len(*q.Results) == 0 {
|
||||||
terms := make( []string, len(*q.SearchTerms), len(*q.SearchTerms) )
|
terms := make([]string, len(*q.SearchTerms), len(*q.SearchTerms))
|
||||||
for i, term := range *q.SearchTerms {
|
for i, term := range *q.SearchTerms {
|
||||||
terms[i] = term.Org
|
terms[i] = term.Org
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf( "Not Found: \"%s\"", strings.Join( terms, "\", \"" ) )
|
return "", fmt.Errorf("Not Found: \"%s\"", strings.Join(terms, "\", \""))
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Key == "" {
|
if q.Key == "" {
|
||||||
sort.Sort( query.ByKey( *q.Results ) )
|
sort.Sort(query.ByKey(*q.Results))
|
||||||
for _, entry := range *q.Results {
|
for _, entry := range *q.Results {
|
||||||
busStop := any( entry ).( *BusStop )
|
busStop := any(entry).(*BusStop)
|
||||||
utils.WriteMDv2Text( &sb, busStop.RouteId )
|
utils.WriteMDv2Text(&sb, busStop.RouteId)
|
||||||
sb.WriteString( " " )
|
sb.WriteString(" ")
|
||||||
writeShortRoute( &this.Lang, &sb, busStop )
|
writeShortRoute(&this.Lang, &sb, busStop)
|
||||||
}
|
}
|
||||||
} else if 1 == len( *q.SearchTerms ) {
|
} else if 1 == len(*q.SearchTerms) {
|
||||||
|
|
||||||
// Route listing
|
// Route listing
|
||||||
st := map[string] *BusStop{}
|
busLines := map[string]map[string]*BusStop{}
|
||||||
keys := [] string{}
|
|
||||||
|
|
||||||
for _, entry := range *q.Results {
|
for _, entry := range *q.Results {
|
||||||
|
|
||||||
busStop := any( entry ).( *BusStop )
|
busStop := any(entry).(*BusStop)
|
||||||
if _, ok := st[ busStop.Direction ]; ok {
|
busRoutes := busLines[busStop.ReferenceId]
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
st[ busStop.Direction ] = busStop
|
if busRoutes == nil {
|
||||||
keys = append( keys, busStop.Direction )
|
busRoutes = make(map[string]*BusStop)
|
||||||
|
busLines[busStop.ReferenceId] = busRoutes
|
||||||
|
}
|
||||||
|
|
||||||
for st[ busStop.Direction ].PrevStop() != nil {
|
if _, ok := busRoutes[busStop.Direction]; ok {
|
||||||
st[ busStop.Direction ] = st[ busStop.Direction ].PrevStop()
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings( keys )
|
busRoutes[busStop.Direction] = busStop
|
||||||
|
|
||||||
for _, d := range keys {
|
for busRoutes[busStop.Direction].PrevStop() != nil {
|
||||||
b := st[ d ]
|
busRoutes[busStop.Direction] = busRoutes[busStop.Direction].PrevStop()
|
||||||
utils.WriteMDv2Text( &sb, q.Key )
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if d == "O" {
|
// Sort Bus Lines
|
||||||
sb.WriteString( "↑" )
|
lineKeys := make([]string, 0, len(busLines))
|
||||||
} else if d == "I" {
|
for k := range busLines {
|
||||||
sb.WriteString( "↓" )
|
lineKeys = append(lineKeys, k)
|
||||||
} else {
|
}
|
||||||
sb.WriteString( "\\?" )
|
|
||||||
}
|
|
||||||
sb.WriteString( "\n " )
|
|
||||||
|
|
||||||
for {
|
sort.Slice(lineKeys, func(i, j int) bool {
|
||||||
utils.WriteMDv2Text( &sb, (*b.Name)[ this.Lang ] )
|
ai, an := splitKey(lineKeys[i])
|
||||||
b = b.NextStop()
|
bi, bn := splitKey(lineKeys[j])
|
||||||
if b == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString( " \\> " )
|
// different base → normal string compare
|
||||||
}
|
if ai != bi {
|
||||||
sb.WriteString( "\n" )
|
return ai < bi
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
// same base: base key comes first
|
||||||
return "", fmt.Errorf( "%s", "Unreachable condition occured!?" )
|
if an == -1 && bn != -1 {
|
||||||
}
|
return true
|
||||||
} else {
|
}
|
||||||
if 0 < len( *this.Schedules ) {
|
if an != -1 && bn == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
busStops := [] *BusStop{}
|
// both have suffix → numeric compare
|
||||||
|
return an < bn
|
||||||
|
})
|
||||||
|
|
||||||
for b, _ := range *this.Schedules {
|
for _, lineKey := range lineKeys {
|
||||||
busStops = append( busStops, b )
|
busRoutes := busLines[lineKey]
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort( ByRoute( busStops ) )
|
// Sort route directions
|
||||||
|
dirKeys := make([]string, 0, len(busRoutes))
|
||||||
|
for k := range busRoutes {
|
||||||
|
dirKeys = append(dirKeys, k)
|
||||||
|
}
|
||||||
|
|
||||||
for _, busStop := range busStops {
|
sort.Strings(dirKeys)
|
||||||
buses := (*this.Schedules)[ busStop ]
|
|
||||||
|
|
||||||
writeShortRoute( &this.Lang, &sb, busStop )
|
for _, d := range dirKeys {
|
||||||
for _, bus := range buses.Buses {
|
b := busRoutes[d]
|
||||||
sb.WriteString( " \\* " )
|
|
||||||
if bus.ETAText == "" {
|
|
||||||
utils.WriteMDv2Text( &sb, bus.ETDText )
|
|
||||||
} else {
|
|
||||||
utils.WriteMDv2Text( &sb, bus.ETAText )
|
|
||||||
}
|
|
||||||
sb.WriteString( "\n" )
|
|
||||||
}
|
|
||||||
sb.WriteString( "\n" )
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
utils.WriteMDv2Text( &sb, "Schedules are empty...perhaps Out of Service Time?" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.String(), nil
|
utils.WriteMDv2Text(&sb, lineKey)
|
||||||
|
|
||||||
|
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])
|
||||||
|
b = b.NextStop()
|
||||||
|
if b == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.WriteString(" \\> ")
|
||||||
|
}
|
||||||
|
sb.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("%s", "Unreachable condition occured!?")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if 0 < len(*this.Schedules) {
|
||||||
|
|
||||||
|
busStops := []*BusStop{}
|
||||||
|
|
||||||
|
for b, _ := range *this.Schedules {
|
||||||
|
busStops = append(busStops, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(ByRoute(busStops))
|
||||||
|
|
||||||
|
for _, busStop := range busStops {
|
||||||
|
buses := (*this.Schedules)[busStop]
|
||||||
|
|
||||||
|
writeShortRoute(&this.Lang, &sb, busStop)
|
||||||
|
for _, bus := range buses.Buses {
|
||||||
|
sb.WriteString(" \\* ")
|
||||||
|
if bus.ETAText == "" {
|
||||||
|
utils.WriteMDv2Text(&sb, bus.ETDText)
|
||||||
|
} else {
|
||||||
|
utils.WriteMDv2Text(&sb, bus.ETAText)
|
||||||
|
}
|
||||||
|
sb.WriteString("\n")
|
||||||
|
}
|
||||||
|
sb.WriteString("\n")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
utils.WriteMDv2Text(&sb, "Schedules are empty...perhaps Out of Service Time?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitKey(s string) (base string, num int) {
|
||||||
|
parts := strings.SplitN(s, "-", 2)
|
||||||
|
if len(parts) == 1 {
|
||||||
|
return s, -1 // base key, no suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return s, -1 // fallback: treat as base
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts[0], n
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -63,21 +62,16 @@ func readBusStopData(r io.Reader) (*map[string]*BusStop, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignoring special route for now as getSchedule does not reflect
|
entryId := fmt.Sprintf("%s:%s", entry.ReferenceId, entry.StationId)
|
||||||
// which bus is a special route
|
|
||||||
if entry.ReferenceId != entry.RouteId {
|
|
||||||
log.Printf("Ignoring special Route: %s", entry.ReferenceId)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if busStops[entry.StationId] != nil {
|
if busStops[entryId] != nil {
|
||||||
return nil, fmt.Errorf("Duplicated entry %+v", entry)
|
return nil, fmt.Errorf("Duplicated entry %+v", entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
routeDir, hasKey := routeStops[entry.RouteId]
|
routeDir, hasKey := routeStops[entry.ReferenceId]
|
||||||
if !hasKey {
|
if !hasKey {
|
||||||
routeStops[entry.RouteId] = map[string]map[int]*BusStop{}
|
routeStops[entry.ReferenceId] = map[string]map[int]*BusStop{}
|
||||||
routeDir = routeStops[entry.RouteId]
|
routeDir = routeStops[entry.ReferenceId]
|
||||||
}
|
}
|
||||||
|
|
||||||
route, hasKey := routeDir[entry.Direction]
|
route, hasKey := routeDir[entry.Direction]
|
||||||
@@ -94,7 +88,7 @@ func readBusStopData(r io.Reader) (*map[string]*BusStop, error) {
|
|||||||
entry.RouteStops = &route
|
entry.RouteStops = &route
|
||||||
entry.Reload()
|
entry.Reload()
|
||||||
|
|
||||||
busStops[entry.StationId] = &entry
|
busStops[entryId] = &entry
|
||||||
}
|
}
|
||||||
return &busStops, nil
|
return &busStops, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,66 @@
|
|||||||
package bus
|
package bus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"log"
|
||||||
query "github.com/tgckpg/golifehk/query"
|
"strings"
|
||||||
|
|
||||||
|
query "github.com/tgckpg/golifehk/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Query( lang string, message string ) query.IQueryResult {
|
func Query(lang string, message string) query.IQueryResult {
|
||||||
|
|
||||||
var qBusStops *query.QueryObject
|
var qBusStops *query.QueryObject
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
qr := QueryResult{ Lang: lang }
|
qr := QueryResult{Lang: lang}
|
||||||
busStops, err := getBusStops()
|
busStops, err := getBusStops()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qr.Error = err
|
qr.Error = err
|
||||||
goto qrReturn
|
goto qrReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
qBusStops, err = query.Parse( strings.ToUpper( message ), busStops )
|
qBusStops, err = query.Parse(strings.ToUpper(message), busStops)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qr.Error = err
|
qr.Error = err
|
||||||
goto qrReturn
|
goto qrReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
qr.Query = qBusStops
|
qr.Query = qBusStops
|
||||||
if 0 < len( *qBusStops.Results ) && 1 < len( *qBusStops.SearchTerms ) {
|
if 0 < len(*qBusStops.Results) && 1 < len(*qBusStops.SearchTerms) {
|
||||||
schedules, err := getSchedule( lang, qBusStops.Key )
|
schedules, err := getSchedule(lang, qBusStops.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qr.Error = err
|
qr.Error = err
|
||||||
goto qrReturn
|
goto qrReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
if len( schedules.BusStops ) == 0 {
|
if len(schedules.BusStops) == 0 {
|
||||||
qr.Schedules = &map[*BusStop] *BusStopBuses{}
|
qr.Schedules = &map[*BusStop]*BusStopBuses{}
|
||||||
goto qrReturn
|
goto qrReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := map[*BusStop] *BusStopBuses{}
|
matches := map[*BusStop]*BusStopBuses{}
|
||||||
for _, entry := range *qBusStops.Results {
|
for _, entry := range *qBusStops.Results {
|
||||||
busStop := any( entry ).( *BusStop )
|
busStop := any(entry).(*BusStop)
|
||||||
|
|
||||||
for _, busStopSch := range schedules.BusStops {
|
for _, busStopSch := range schedules.BusStops {
|
||||||
if busStopSch.BusStopId == busStop.StationId {
|
if busStopSch.BusStopId == busStop.StationId {
|
||||||
matches[busStop] = &busStopSch
|
if busStop.RouteId != busStop.ReferenceId {
|
||||||
break
|
// There were no indicator for special routes from getSchedule API
|
||||||
}
|
log.Printf("Ignoring special route matches: %s", busStop.ReferenceId)
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
matches[busStop] = &busStopSch
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qr.Schedules = &matches
|
qr.Schedules = &matches
|
||||||
}
|
}
|
||||||
|
|
||||||
qrReturn:
|
qrReturn:
|
||||||
var iqr query.IQueryResult
|
var iqr query.IQueryResult
|
||||||
iqr = &qr
|
iqr = &qr
|
||||||
return iqr
|
return iqr
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user