Properly handle REFERENCE_ID for future changes

This commit is contained in:
2025-12-16 20:17:45 +08:00
parent 6e94adbed4
commit 36e077c910
4 changed files with 227 additions and 165 deletions

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -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
} }