Support route listing & search

This commit is contained in:
斟酌 鵬兄 2022-09-15 20:00:49 +08:00
parent 9724abe2e2
commit 89770c0864
3 changed files with 178 additions and 61 deletions

View File

@ -2,6 +2,7 @@ package bus
import ( import (
"fmt" "fmt"
"sort"
"strings" "strings"
) )
@ -9,8 +10,33 @@ type QueryResult struct {
Schedules *map[BusStop] *BusStopBuses Schedules *map[BusStop] *BusStopBuses
Lang string Lang string
Error error Error error
Query *QueryObject
} }
func writeShortRoute( lang *string, sb *strings.Builder, b *BusStop ) {
if b.PrevStop() != nil {
sb.WriteString( (*b.PrevStop().Name)[ *lang ] )
sb.WriteString( " > " )
}
sb.WriteString( "**" )
sb.WriteString( (*b.Name)[ *lang ] )
sb.WriteString( "**" )
if b.NextStop() != nil {
sb.WriteString( " > " )
sb.WriteString( (*b.NextStop().Name)[ *lang ] )
}
sb.WriteString( "\n" )
}
type ByRouteId []BusStop
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 }
func ( result QueryResult ) Message() string { func ( result QueryResult ) Message() string {
if result.Error != nil { if result.Error != nil {
@ -19,33 +45,51 @@ func ( result QueryResult ) Message() string {
sb := strings.Builder{} sb := strings.Builder{}
for busStop, buses := range *result.Schedules { if result.Schedules == nil {
if busStop.PrevStop() != nil { query := *result.Query
sb.WriteString( (*busStop.PrevStop().Name)[ result.Lang ] ) if query.Route == "" {
sb.WriteString( " > *" ) sort.Sort( ByRouteId( *query.BusStops ) )
for _, busStop := range *query.BusStops {
sb.WriteString( busStop.RouteId )
sb.WriteString( " " )
writeShortRoute( &result.Lang, &sb, &busStop )
}
} else if query.BusStops == nil && query.RouteStarts != nil {
for d, b := range *query.RouteStarts {
sb.WriteString( query.Route )
sb.WriteString( "-" )
sb.WriteString( d )
sb.WriteString( "\n " )
for {
sb.WriteString( (*b.Name)[ result.Lang ] )
b = b.NextStop()
if b == nil {
break
}
sb.WriteString( " > " )
}
sb.WriteString( "\n" )
}
} else {
sb.WriteString( "Unreachable condition occured!?" )
} }
} else {
sb.WriteString( (*busStop.Name)[ result.Lang ] ) for busStop, buses := range *result.Schedules {
sb.WriteString( "*" ) writeShortRoute( &result.Lang, &sb, &busStop )
for _, bus := range buses.Buses {
if busStop.NextStop() != nil { sb.WriteString( " * " )
sb.WriteString( " > " ) if bus.ETAText == "" {
sb.WriteString( (*busStop.NextStop().Name)[ result.Lang ] ) sb.WriteString( bus.ETDText )
} } else {
sb.WriteString( bus.ETAText )
sb.WriteString( "\n" ) }
for _, bus := range buses.Buses { sb.WriteString( "\n" )
sb.WriteString( " * " )
if bus.ETAText == "" {
sb.WriteString( bus.ETDText )
} else {
sb.WriteString( bus.ETAText )
} }
sb.WriteString( "\n" ) sb.WriteString( "\n" )
} }
sb.WriteString( "\n" )
} }
return sb.String() return sb.String()

View File

@ -7,37 +7,66 @@ import (
"github.com/tgckpg/golifehk/utils" "github.com/tgckpg/golifehk/utils"
) )
type queryObj struct {
Route string
BusStops *[]BusStop
Search string
}
type qTerm struct { type qTerm struct {
Org string
Value string Value string
ProblyRoute bool ProblyRoute bool
} }
type QueryObject struct {
Route string
BusStops *[]BusStop
RouteStarts *map[string] *BusStop
SearchTerms *[] *qTerm
}
func NotFound ( terms *[] *qTerm ) string {
sb := strings.Builder{}
sb.WriteString( "Not Found" )
if 0 < len( *terms ) {
sb.WriteString( ":" )
for _, term := range *terms {
sb.WriteString( " \"" )
sb.WriteString( term.Org )
sb.WriteString( "\"" )
}
}
return sb.String()
}
func Query( lang string, message string ) *QueryResult { func Query( lang string, message string ) *QueryResult {
qr := QueryResult{ Lang: lang }
qo, err := parse( message ) qo, err := parse( message )
if err != nil { if err != nil {
return &QueryResult{ Error: err } qr.Error = err
return &qr
} }
qr.Query = qo
if qo.Route != "" { if qo.Route != "" {
if qo.BusStops == nil {
return &qr
}
if len( *qo.BusStops ) == 0 { if len( *qo.BusStops ) == 0 {
return &QueryResult{ Error: errors.New( "Result not found" ) } qr.Error = errors.New( NotFound( qo.SearchTerms ) )
return &qr
} }
schedules, err := getSchedule( lang, qo.Route ) schedules, err := getSchedule( lang, qo.Route )
if err != nil { if err != nil {
return &QueryResult{ Error: err } qr.Error = err
return &qr
} }
if len( schedules.BusStops ) == 0 { if len( schedules.BusStops ) == 0 {
return &QueryResult{ Error: errors.New( "Schedules are empty. Maybe outside service time?" ) } qr.Error = errors.New( "Schedules are empty...perhaps Out of Service Time?" )
return &qr
} }
matches := map[BusStop] *BusStopBuses{} matches := map[BusStop] *BusStopBuses{}
@ -52,10 +81,16 @@ func Query( lang string, message string ) *QueryResult {
} }
return &QueryResult{ Lang: lang, Schedules: &matches } qr.Schedules = &matches
return &qr
} else {
if len( *qo.BusStops ) == 0 {
return &QueryResult{ Error: errors.New( NotFound( qo.SearchTerms ) ) }
}
return &qr
} }
return &QueryResult{ Error: errors.New( "No Result" ) }
} }
func test( entry BusStop, val string ) bool { func test( entry BusStop, val string ) bool {
@ -68,7 +103,7 @@ func test( entry BusStop, val string ) bool {
return false return false
} }
func parse( line string ) ( *queryObj, error ) { func parse( line string ) ( *QueryObject, error ) {
busStops, err := getBusStops() busStops, err := getBusStops()
if err != nil { if err != nil {
@ -76,18 +111,22 @@ func parse( line string ) ( *queryObj, error ) {
} }
var route string = "" var route string = ""
var searches = []string{}
matches := []BusStop{} matches := []BusStop{}
// Sanitize and assume properties for each of the keywords // Sanitize and assume properties for each of the keywords
terms := []qTerm{} terms := [] *qTerm{}
for _, val := range strings.Split( line, " " ) { for _, val := range strings.Split( line, " " ) {
val = strings.ToUpper( strings.Trim( val, " " ) )
if val == "" {
continue
}
term := qTerm{ term := qTerm{
Value: val, Org: val,
Value: strings.ToUpper( strings.Trim( val, " " ) ),
ProblyRoute: strings.ContainsAny( val, utils.ROUTE_CHARS ), ProblyRoute: strings.ContainsAny( val, utils.ROUTE_CHARS ),
} }
terms = append( terms, term ) terms = append( terms, &term )
} }
// Search for route name first, otherwise search in other props // Search for route name first, otherwise search in other props
@ -105,7 +144,6 @@ func parse( line string ) ( *queryObj, error ) {
break break
} }
searches = append( searches, term.Value )
if test( entry, term.Value ) { if test( entry, term.Value ) {
matches = append( matches, entry ) matches = append( matches, entry )
break break
@ -113,27 +151,62 @@ func parse( line string ) ( *queryObj, error ) {
} }
} }
// If route found, filter out all other route searchTerms := [] *qTerm{}
// then search within that route matches_in := []BusStop{}
if route != "" {
if 0 < len( searches ) {
matches_in := []BusStop{}
for _, entry := range matches {
if entry.RouteId != route {
continue
}
for _, val := range searches { for _, term := range terms {
if test( entry, val ) { if term.ProblyRoute {
matches_in = append( matches_in, entry ) continue
break
}
}
}
matches = matches_in
} else {
} }
searchTerms = append( searchTerms, term )
} }
return &queryObj{ Route: route, BusStops: &matches }, err if 0 < len( searchTerms ) {
// If route found, filter out all other route
// then search the terms within that route
for _, entry := range matches {
if route != "" && entry.RouteId != route {
continue
}
for _, term := range searchTerms {
if test( entry, term.Value ) {
matches_in = append( matches_in, entry )
break
}
}
}
matches = matches_in
return &QueryObject{
Route: route,
BusStops: &matches,
SearchTerms: &searchTerms,
}, err
} else if route != "" {
// Route listing
st := map[string] *BusStop{}
for _, entry := range *busStops {
if entry.RouteId != route {
continue
}
if _, ok := st[ entry.Direction ]; ok {
continue
}
st[ entry.Direction ] = &entry
for st[ entry.Direction ].PrevStop() != nil {
st[ entry.Direction ] = st[ entry.Direction ].PrevStop()
}
}
return &QueryObject{ Route: route, RouteStarts: &st, BusStops: nil }, nil
}
return nil, fmt.Errorf( "Cannot parse: %s", line )
} }

View File

@ -6,7 +6,7 @@ import (
) )
func TestQuerySchedule( t *testing.T ) { func TestQuerySchedule( t *testing.T ) {
qo := Query( "zh", "K73 池" ) qo := Query( "zh", "" )
fmt.Print( qo.Message() ) fmt.Print( qo.Message() )
} }