Support route listing & search
This commit is contained in:
parent
9724abe2e2
commit
89770c0864
@ -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()
|
||||||
|
@ -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 )
|
||||||
}
|
}
|
||||||
|
@ -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() )
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user