Support route listing & search
This commit is contained in:
		@@ -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,22 +45,40 @@ 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( (*busStop.Name)[ result.Lang ] )
 | 
					 | 
				
			||||||
        sb.WriteString( "*" )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if busStop.NextStop() != nil {
 | 
					 | 
				
			||||||
                    sb.WriteString( " > " )
 | 
					                    sb.WriteString( " > " )
 | 
				
			||||||
            sb.WriteString( (*busStop.NextStop().Name)[ result.Lang ] )
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                sb.WriteString( "\n" )
 | 
					                sb.WriteString( "\n" )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            sb.WriteString( "Unreachable condition occured!?" )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        for busStop, buses := range *result.Schedules {
 | 
				
			||||||
 | 
					            writeShortRoute( &result.Lang, &sb, &busStop )
 | 
				
			||||||
            for _, bus := range buses.Buses {
 | 
					            for _, bus := range buses.Buses {
 | 
				
			||||||
                sb.WriteString( "  * " )
 | 
					                sb.WriteString( "  * " )
 | 
				
			||||||
                if bus.ETAText == "" {
 | 
					                if bus.ETAText == "" {
 | 
				
			||||||
@@ -44,9 +88,9 @@ func ( result QueryResult ) Message() string {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                sb.WriteString( "\n" )
 | 
					                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
 | 
					 | 
				
			||||||
    if route != "" {
 | 
					 | 
				
			||||||
        if 0 < len( searches ) {
 | 
					 | 
				
			||||||
    matches_in := []BusStop{}
 | 
					    matches_in := []BusStop{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for _, term := range terms {
 | 
				
			||||||
 | 
					        if term.ProblyRoute {
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        searchTerms = append( searchTerms, term )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if 0 < len( searchTerms ) {
 | 
				
			||||||
 | 
					        // If route found, filter out all other route
 | 
				
			||||||
 | 
					        // then search the terms within that route
 | 
				
			||||||
        for _, entry := range matches {
 | 
					        for _, entry := range matches {
 | 
				
			||||||
                if entry.RouteId != route {
 | 
					            if route != "" && entry.RouteId != route {
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for _, val := range searches {
 | 
					            for _, term := range searchTerms {
 | 
				
			||||||
                    if test( entry, val ) {
 | 
					                if test( entry, term.Value ) {
 | 
				
			||||||
                    matches_in = append( matches_in, entry )
 | 
					                    matches_in = append( matches_in, entry )
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        matches = matches_in
 | 
					        matches = matches_in
 | 
				
			||||||
        } else {
 | 
					
 | 
				
			||||||
 | 
					        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 &queryObj{ Route: route, BusStops: &matches }, err
 | 
					        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() )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user