Added kmb and refactored query.Parse
This commit is contained in:
		@@ -1,5 +1,9 @@
 | 
			
		||||
package bus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
    i18n "github.com/tgckpg/golifehk/i18n"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BusStop struct {
 | 
			
		||||
    RouteId string
 | 
			
		||||
    Direction string
 | 
			
		||||
@@ -7,27 +11,39 @@ type BusStop struct {
 | 
			
		||||
    StationId string
 | 
			
		||||
    Latitude float64
 | 
			
		||||
    Longtitude float64
 | 
			
		||||
    Name_zh string
 | 
			
		||||
    Name_en string
 | 
			
		||||
 | 
			
		||||
    Name *map[string] string
 | 
			
		||||
    // RouteStops[ StationSeq ] = BusStop
 | 
			
		||||
    RouteStops *map[int] *BusStop
 | 
			
		||||
 | 
			
		||||
    i18n.Generics
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ( busStop BusStop ) PrevStop() *BusStop {
 | 
			
		||||
    if v, hasKey := (*busStop.RouteStops)[ busStop.StationSeq - 1 ]; hasKey {
 | 
			
		||||
func ( this *BusStop ) PrevStop() *BusStop {
 | 
			
		||||
    if v, hasKey := (*this.RouteStops)[ this.StationSeq - 1 ]; hasKey {
 | 
			
		||||
        return v
 | 
			
		||||
    }
 | 
			
		||||
    return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ( busStop BusStop ) NextStop() *BusStop {
 | 
			
		||||
    if v, hasKey := (*busStop.RouteStops)[ busStop.StationSeq + 1 ]; hasKey {
 | 
			
		||||
func ( this *BusStop ) NextStop() *BusStop {
 | 
			
		||||
    if v, hasKey := (*this.RouteStops)[ this.StationSeq + 1 ]; hasKey {
 | 
			
		||||
        return v
 | 
			
		||||
    }
 | 
			
		||||
    return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ByRouteId []BusStop
 | 
			
		||||
func ( this *BusStop ) Reload() {
 | 
			
		||||
    i18n_Name := map[string] string{}
 | 
			
		||||
    i18n_Name["en"] = this.Name_en
 | 
			
		||||
    i18n_Name["zh-Hant"] = this.Name_zh
 | 
			
		||||
 | 
			
		||||
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 }
 | 
			
		||||
    searchData := [] *string{}
 | 
			
		||||
    searchData = append( searchData, &this.Name_en )
 | 
			
		||||
    searchData = append( searchData, &this.Name_zh )
 | 
			
		||||
 | 
			
		||||
    this.Name = &i18n_Name
 | 
			
		||||
    this.Key = &this.RouteId
 | 
			
		||||
    this.SearchData = &searchData
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,68 +4,87 @@ import (
 | 
			
		||||
    "fmt"
 | 
			
		||||
    "sort"
 | 
			
		||||
    "strings"
 | 
			
		||||
 | 
			
		||||
    query "github.com/tgckpg/golifehk/query"
 | 
			
		||||
    utils "github.com/tgckpg/golifehk/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type QueryResult struct {
 | 
			
		||||
    Schedules *map[BusStop] *BusStopBuses
 | 
			
		||||
    Schedules *map[*BusStop] *BusStopBuses
 | 
			
		||||
    Lang string
 | 
			
		||||
    Error error
 | 
			
		||||
    Query *QueryObject
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var MARKDOWN_ESC []string = []string{"_", "*", "[", "]", "(", ")", "~", "`", ">", "#", "+", "-", "=", "|", "{", "}", ".", "!"}
 | 
			
		||||
func _escape( t string ) string {
 | 
			
		||||
    for _, c := range MARKDOWN_ESC {
 | 
			
		||||
        t = strings.Replace( t, c, "\\" + c, -1 )
 | 
			
		||||
    }
 | 
			
		||||
    return t
 | 
			
		||||
    Query *query.QueryObject
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeShortRoute( lang *string, sb *strings.Builder, b *BusStop ) {
 | 
			
		||||
    if b.PrevStop() != nil {
 | 
			
		||||
        sb.WriteString( _escape( (*b.PrevStop().Name)[ *lang ] ) )
 | 
			
		||||
        utils.WriteMDv2Text( sb, (*b.PrevStop().Name)[ *lang ] ) 
 | 
			
		||||
        sb.WriteString( " \\> " )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sb.WriteString( "*" )
 | 
			
		||||
    sb.WriteString( _escape( (*b.Name)[ *lang ] ) )
 | 
			
		||||
    utils.WriteMDv2Text( sb, (*b.Name)[ *lang ] ) 
 | 
			
		||||
    sb.WriteString( "*" )
 | 
			
		||||
 | 
			
		||||
    if b.NextStop() != nil {
 | 
			
		||||
        sb.WriteString( " \\> " )
 | 
			
		||||
        sb.WriteString( _escape( (*b.NextStop().Name)[ *lang ] ) )
 | 
			
		||||
        utils.WriteMDv2Text( sb, (*b.NextStop().Name)[ *lang ] ) 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sb.WriteString( "\n" )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ( result QueryResult ) Message() string {
 | 
			
		||||
func ( this QueryResult ) Message() ( string, error ) {
 | 
			
		||||
 | 
			
		||||
    if result.Error != nil {
 | 
			
		||||
        return fmt.Sprintf( "%s", result.Error )
 | 
			
		||||
    if this.Error != nil {
 | 
			
		||||
        return "", this.Error
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sb := strings.Builder{}
 | 
			
		||||
 | 
			
		||||
    if result.Schedules == nil {
 | 
			
		||||
    if this.Schedules == nil {
 | 
			
		||||
 | 
			
		||||
        query := *result.Query
 | 
			
		||||
        if query.Route == "" {
 | 
			
		||||
            sort.Sort( ByRouteId( *query.BusStops ) )
 | 
			
		||||
            for _, busStop := range *query.BusStops {
 | 
			
		||||
                sb.WriteString( _escape( busStop.RouteId ) )
 | 
			
		||||
        q := *this.Query
 | 
			
		||||
        if q.Key == "" {
 | 
			
		||||
            sort.Sort( query.ByKey( *q.Results ) )
 | 
			
		||||
            for _, entry := range *q.Results {
 | 
			
		||||
                busStop := any( entry ).( *BusStop )
 | 
			
		||||
                utils.WriteMDv2Text( &sb, busStop.RouteId ) 
 | 
			
		||||
                sb.WriteString( " " )
 | 
			
		||||
                writeShortRoute( &result.Lang, &sb, &busStop )
 | 
			
		||||
                writeShortRoute( &this.Lang, &sb, busStop )
 | 
			
		||||
            }
 | 
			
		||||
        } else if query.BusStops == nil && query.RouteStarts != nil {
 | 
			
		||||
            for d, b := range *query.RouteStarts {
 | 
			
		||||
                sb.WriteString( query.Route )
 | 
			
		||||
        } else if 1 == len( *q.SearchTerms ) && 0 < len( *q.Results ) {
 | 
			
		||||
 | 
			
		||||
            // Route listing
 | 
			
		||||
            st := map[string] *BusStop{}
 | 
			
		||||
            keys := [] string{}
 | 
			
		||||
 | 
			
		||||
            for _, entry := range *q.Results {
 | 
			
		||||
 | 
			
		||||
                busStop := any( entry ).( *BusStop )
 | 
			
		||||
                if _, ok := st[ busStop.Direction ]; ok {
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                st[ busStop.Direction ] = busStop
 | 
			
		||||
                keys = append( keys, busStop.Direction )
 | 
			
		||||
 | 
			
		||||
                for st[ busStop.Direction ].PrevStop() != nil {
 | 
			
		||||
                    st[ busStop.Direction ] = st[ busStop.Direction ].PrevStop()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sort.Strings( keys )
 | 
			
		||||
 | 
			
		||||
            for _, d := range keys {
 | 
			
		||||
                b := st[ d ]
 | 
			
		||||
                sb.WriteString( q.Key )
 | 
			
		||||
                sb.WriteString( "\\-" )
 | 
			
		||||
                sb.WriteString( _escape( d ) )
 | 
			
		||||
                utils.WriteMDv2Text( &sb, d ) 
 | 
			
		||||
                sb.WriteString( "\n " )
 | 
			
		||||
 | 
			
		||||
                for {
 | 
			
		||||
                    sb.WriteString( _escape( (*b.Name)[ result.Lang ] ) )
 | 
			
		||||
                    utils.WriteMDv2Text( &sb, (*b.Name)[ this.Lang ] ) 
 | 
			
		||||
                    b = b.NextStop()
 | 
			
		||||
                    if b == nil {
 | 
			
		||||
                        break
 | 
			
		||||
@@ -75,28 +94,35 @@ func ( result QueryResult ) Message() string {
 | 
			
		||||
                }
 | 
			
		||||
                sb.WriteString( "\n" )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else if 1 < len( *q.SearchTerms ) && len( *q.Results ) == 0 {
 | 
			
		||||
            terms := make( []string, len(*q.SearchTerms), len(*q.SearchTerms) )
 | 
			
		||||
            for i, term := range *q.SearchTerms {
 | 
			
		||||
                terms[i] = term.Org
 | 
			
		||||
            }
 | 
			
		||||
            return "", fmt.Errorf( "Not Found: \"%s\"", strings.Join( terms, "\", \"" ) )
 | 
			
		||||
        } else {
 | 
			
		||||
            sb.WriteString( _escape( "Unreachable condition occured!?" ) )
 | 
			
		||||
            return "", fmt.Errorf( "%s", "Unreachable condition occured!?" )
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if 0 < len(*result.Schedules) {
 | 
			
		||||
            for busStop, buses := range *result.Schedules {
 | 
			
		||||
                writeShortRoute( &result.Lang, &sb, &busStop )
 | 
			
		||||
        if 0 < len( *this.Schedules ) {
 | 
			
		||||
            for busStop, buses := range *this.Schedules {
 | 
			
		||||
                writeShortRoute( &this.Lang, &sb, busStop )
 | 
			
		||||
                for _, bus := range buses.Buses {
 | 
			
		||||
                    sb.WriteString( "  \\* " )
 | 
			
		||||
                    if bus.ETAText == "" {
 | 
			
		||||
                        sb.WriteString( _escape( bus.ETDText ) )
 | 
			
		||||
                        utils.WriteMDv2Text( &sb, bus.ETDText ) 
 | 
			
		||||
                    } else {
 | 
			
		||||
                        sb.WriteString( _escape( bus.ETAText ) )
 | 
			
		||||
                        utils.WriteMDv2Text( &sb, bus.ETAText ) 
 | 
			
		||||
                    }
 | 
			
		||||
                    sb.WriteString( "\n" )
 | 
			
		||||
                }
 | 
			
		||||
                sb.WriteString( "\n" )
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            sb.WriteString( _escape( "Schedules are empty...perhaps Out of Service Time?" ) )
 | 
			
		||||
            utils.WriteMDv2Text( &sb, "Schedules are empty...perhaps Out of Service Time?" ) 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sb.String()
 | 
			
		||||
    return sb.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,15 @@ func getSchedule( lang string, routeName string ) ( *BusSchedule, error ) {
 | 
			
		||||
        "mtr_bsch" + "-" + lang + "-" + routeName + ".json",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    postLang := "en"
 | 
			
		||||
    if lang == "zh-Hant" {
 | 
			
		||||
        postLang = "zh"
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QUERY_FUNC := func() ( io.ReadCloser, error ) {
 | 
			
		||||
        // Query Remote
 | 
			
		||||
        values := map[string]string { "language": lang , "routeName": routeName }
 | 
			
		||||
        values := map[string]string { "language": postLang, "routeName": routeName }
 | 
			
		||||
        jsonValue, _ := json.Marshal(values)
 | 
			
		||||
        resp, err := http.Post(
 | 
			
		||||
            "https://rt.data.gov.hk/v1/transport/mtr/bus/getSchedule",
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,13 @@ import (
 | 
			
		||||
    "strconv"
 | 
			
		||||
    "strings"
 | 
			
		||||
 | 
			
		||||
    query "github.com/tgckpg/golifehk/query"
 | 
			
		||||
    "github.com/tgckpg/golifehk/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var CSV_BUSSTOPS string = filepath.Join( utils.WORKDIR, "mtr_bus_stops.csv" )
 | 
			
		||||
 | 
			
		||||
func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 | 
			
		||||
func readBusStopData( r io.Reader ) ( *map[string] *BusStop, error ) {
 | 
			
		||||
 | 
			
		||||
    reader := csv.NewReader( r )
 | 
			
		||||
    entries, err := reader.ReadAll()
 | 
			
		||||
@@ -22,7 +23,7 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 | 
			
		||||
        return nil, err
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    busStops := map[string]BusStop{}
 | 
			
		||||
    busStops := map[string] *BusStop{}
 | 
			
		||||
    routeStops := map[string] map[string] map[int] *BusStop{}
 | 
			
		||||
 | 
			
		||||
    var headers []string
 | 
			
		||||
@@ -34,7 +35,6 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var entry BusStop
 | 
			
		||||
        i18n_Name := map[string] string{}
 | 
			
		||||
 | 
			
		||||
        for j, value := range line {
 | 
			
		||||
            switch headers[j] {
 | 
			
		||||
@@ -54,15 +54,15 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 | 
			
		||||
                    v, _ := strconv.ParseFloat( value, 64 )
 | 
			
		||||
                    entry.Longtitude = v
 | 
			
		||||
                case "STATION_NAME_CHI":
 | 
			
		||||
                    i18n_Name["zh"] = value
 | 
			
		||||
                    entry.Name_zh = value
 | 
			
		||||
                case "STATION_NAME_ENG":
 | 
			
		||||
                    i18n_Name["en"] = value
 | 
			
		||||
                    entry.Name_en = value
 | 
			
		||||
                default:
 | 
			
		||||
                    return nil, fmt.Errorf( "Unknown header \"%s\"", headers[j] )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if _, t := busStops[ entry.StationId ]; t {
 | 
			
		||||
        if busStops[ entry.StationId ] != nil {
 | 
			
		||||
            return nil, fmt.Errorf( "Duplicated entry %+v", entry )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -83,15 +83,15 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 | 
			
		||||
            route[ entry.StationSeq ] = &entry
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        entry.Name = &i18n_Name
 | 
			
		||||
        entry.RouteStops = &route
 | 
			
		||||
        entry.Reload()
 | 
			
		||||
 | 
			
		||||
        busStops[ entry.StationId ] = entry
 | 
			
		||||
        busStops[ entry.StationId ] = &entry
 | 
			
		||||
    }
 | 
			
		||||
    return &busStops, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getBusStops() (*map[string]BusStop, error) {
 | 
			
		||||
func getBusStops() (*[] query.ISearchable, error) {
 | 
			
		||||
 | 
			
		||||
    QUERY_FUNC := func() ( io.ReadCloser, error ) {
 | 
			
		||||
        resp, err := http.Get( "https://opendata.mtr.com.hk/data/mtr_bus_stops.csv" )
 | 
			
		||||
@@ -106,5 +106,16 @@ func getBusStops() (*map[string]BusStop, error) {
 | 
			
		||||
        return nil, err
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return readBusStopData( buff )
 | 
			
		||||
    busStopMap, err := readBusStopData( buff )
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        return nil, err
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    searchables := [] query.ISearchable{}
 | 
			
		||||
    for _, busStop := range *busStopMap {
 | 
			
		||||
        searchables = append( searchables, busStop )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return &searchables, nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,76 +1,44 @@
 | 
			
		||||
package bus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
    "errors"
 | 
			
		||||
    "fmt"
 | 
			
		||||
    "strings"
 | 
			
		||||
    "github.com/tgckpg/golifehk/utils"
 | 
			
		||||
    query "github.com/tgckpg/golifehk/query"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type qTerm struct {
 | 
			
		||||
    Org string
 | 
			
		||||
    Value string
 | 
			
		||||
    ProblyRoute bool
 | 
			
		||||
}
 | 
			
		||||
func Query( lang string, message string ) query.IQueryResult {
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
    var qBusStops *query.QueryObject
 | 
			
		||||
    var err error
 | 
			
		||||
 | 
			
		||||
    qr := QueryResult{ Lang: lang }
 | 
			
		||||
 | 
			
		||||
    qo, err := parse( message )
 | 
			
		||||
    busStops, err := getBusStops()
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        qr.Error = err
 | 
			
		||||
        return &qr
 | 
			
		||||
        goto qrReturn
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qr.Query = qo
 | 
			
		||||
    qBusStops, err = query.Parse( strings.ToUpper( message ), busStops )
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        qr.Error = err
 | 
			
		||||
        goto qrReturn
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if qo.Route != "" {
 | 
			
		||||
 | 
			
		||||
        if qo.BusStops == nil {
 | 
			
		||||
            return &qr
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if len( *qo.BusStops ) == 0 {
 | 
			
		||||
            qr.Error = errors.New( NotFound( qo.SearchTerms ) )
 | 
			
		||||
            return &qr
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        schedules, err := getSchedule( lang, qo.Route )
 | 
			
		||||
    qr.Query = qBusStops
 | 
			
		||||
    if 0 < len( *qBusStops.Results ) && 1 < len( *qBusStops.SearchTerms ) {
 | 
			
		||||
        schedules, err := getSchedule( lang, qBusStops.Key )
 | 
			
		||||
        if err != nil {
 | 
			
		||||
            qr.Error = err
 | 
			
		||||
            return &qr
 | 
			
		||||
            goto qrReturn
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if len( schedules.BusStops ) == 0 {
 | 
			
		||||
            qr.Schedules = &map[BusStop] *BusStopBuses{}
 | 
			
		||||
            return &qr
 | 
			
		||||
            qr.Schedules = &map[*BusStop] *BusStopBuses{}
 | 
			
		||||
            goto qrReturn
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        matches := map[BusStop] *BusStopBuses{}
 | 
			
		||||
        for _, busStop := range *qo.BusStops {
 | 
			
		||||
        matches := map[*BusStop] *BusStopBuses{}
 | 
			
		||||
        for _, entry := range *qBusStops.Results {
 | 
			
		||||
            busStop := any( entry ).( *BusStop )
 | 
			
		||||
 | 
			
		||||
            for _, busStopSch := range schedules.BusStops {
 | 
			
		||||
                if busStopSch.BusStopId == busStop.StationId {
 | 
			
		||||
@@ -82,131 +50,10 @@ func Query( lang string, message string ) *QueryResult {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        qr.Schedules = &matches
 | 
			
		||||
        return &qr
 | 
			
		||||
    } else {
 | 
			
		||||
        if len( *qo.BusStops ) == 0 {
 | 
			
		||||
            return &QueryResult{ Error: errors.New( NotFound( qo.SearchTerms ) ) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return &qr
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func test( entry BusStop, val string ) bool {
 | 
			
		||||
    switch true {
 | 
			
		||||
    case strings.Contains( strings.ToUpper( (*entry.Name)["zh"] ), val ):
 | 
			
		||||
        fallthrough
 | 
			
		||||
    case strings.Contains( strings.ToUpper( (*entry.Name)["en"] ), val ):
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parse( line string ) ( *QueryObject, error ) {
 | 
			
		||||
    busStops, err := getBusStops()
 | 
			
		||||
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        return nil, err
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var route string = ""
 | 
			
		||||
    matches := []BusStop{}
 | 
			
		||||
 | 
			
		||||
    // Sanitize and assume properties for each of the keywords
 | 
			
		||||
    terms := [] *qTerm{}
 | 
			
		||||
    for _, val := range strings.Split( line, " " ) {
 | 
			
		||||
 | 
			
		||||
        if val == "" {
 | 
			
		||||
            continue
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        term := qTerm{
 | 
			
		||||
            Org: val,
 | 
			
		||||
            Value: strings.ToUpper( strings.Trim( val, " " ) ),
 | 
			
		||||
            ProblyRoute: strings.ContainsAny( val, utils.ROUTE_CHARS ),
 | 
			
		||||
        }
 | 
			
		||||
        terms = append( terms, &term )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Search for route name first, otherwise search in other props
 | 
			
		||||
    for _, entry := range *busStops {
 | 
			
		||||
 | 
			
		||||
        // Search for RouteId
 | 
			
		||||
        for _, term := range terms {
 | 
			
		||||
 | 
			
		||||
            if term.ProblyRoute && term.Value == entry.RouteId {
 | 
			
		||||
                if route != "" && route != term.Value {
 | 
			
		||||
                    return nil, fmt.Errorf( "Cannot %s & %s", route, term.Value )
 | 
			
		||||
                }
 | 
			
		||||
                matches = append( matches, entry )
 | 
			
		||||
                route = entry.RouteId
 | 
			
		||||
                break 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if test( entry, term.Value ) {
 | 
			
		||||
                matches = append( matches, entry )
 | 
			
		||||
                break
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    searchTerms := [] *qTerm{}
 | 
			
		||||
    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 {
 | 
			
		||||
            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 )
 | 
			
		||||
    qrReturn:
 | 
			
		||||
    var iqr query.IQueryResult
 | 
			
		||||
    iqr = &qr
 | 
			
		||||
    return iqr
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,32 @@ package bus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
    "fmt"
 | 
			
		||||
    "strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestQuerySchedule( t *testing.T ) {
 | 
			
		||||
    qo := Query( "zh", "水" )
 | 
			
		||||
func TestQuery( t *testing.T ) {
 | 
			
		||||
    qo := Query( "zh-Hant", "K73" )
 | 
			
		||||
    mesg, err := qo.Message()
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        t.Errorf( "Unexpected Error: %s", err )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fmt.Print( qo.Message() )
 | 
			
		||||
    if !strings.Contains( mesg, "K73\\-O" ) {
 | 
			
		||||
        t.Errorf( "Expected Route Listing, got \"%s\" instead", mesg )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qo = Query( "zh-Hant", "K76 池" )
 | 
			
		||||
    mesg, err = qo.Message()
 | 
			
		||||
    if err == nil {
 | 
			
		||||
        t.Errorf( "Expecting error, got \"%s\" instead", mesg )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qo = Query( "zh-Hant", "K73 池" )
 | 
			
		||||
    mesg, err = qo.Message()
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        t.Errorf( "Unexpected Error: %s", err )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fmt.Println( mesg )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user