2022-09-14 09:12:48 +00:00
|
|
|
package bus
|
2022-09-13 13:42:51 +00:00
|
|
|
|
|
|
|
import (
|
2022-09-14 13:21:39 +00:00
|
|
|
"errors"
|
2022-09-13 13:42:51 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"github.com/tgckpg/golifehk/utils"
|
|
|
|
)
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
type qTerm struct {
|
|
|
|
Org string
|
|
|
|
Value string
|
|
|
|
ProblyRoute bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type QueryObject struct {
|
2022-09-13 13:42:51 +00:00
|
|
|
Route string
|
2022-09-14 09:12:48 +00:00
|
|
|
BusStops *[]BusStop
|
2022-09-15 12:00:49 +00:00
|
|
|
RouteStarts *map[string] *BusStop
|
|
|
|
SearchTerms *[] *qTerm
|
2022-09-14 09:12:48 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
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()
|
2022-09-13 13:42:51 +00:00
|
|
|
}
|
|
|
|
|
2022-09-14 13:21:39 +00:00
|
|
|
func Query( lang string, message string ) *QueryResult {
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
qr := QueryResult{ Lang: lang }
|
|
|
|
|
2022-09-14 13:21:39 +00:00
|
|
|
qo, err := parse( message )
|
|
|
|
if err != nil {
|
2022-09-15 12:00:49 +00:00
|
|
|
qr.Error = err
|
|
|
|
return &qr
|
2022-09-14 13:21:39 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
qr.Query = qo
|
|
|
|
|
2022-09-14 13:21:39 +00:00
|
|
|
if qo.Route != "" {
|
2022-09-14 14:54:51 +00:00
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
if qo.BusStops == nil {
|
|
|
|
return &qr
|
|
|
|
}
|
|
|
|
|
2022-09-14 14:54:51 +00:00
|
|
|
if len( *qo.BusStops ) == 0 {
|
2022-09-15 12:00:49 +00:00
|
|
|
qr.Error = errors.New( NotFound( qo.SearchTerms ) )
|
|
|
|
return &qr
|
2022-09-14 14:54:51 +00:00
|
|
|
}
|
|
|
|
|
2022-09-14 13:21:39 +00:00
|
|
|
schedules, err := getSchedule( lang, qo.Route )
|
|
|
|
if err != nil {
|
2022-09-15 12:00:49 +00:00
|
|
|
qr.Error = err
|
|
|
|
return &qr
|
2022-09-14 13:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len( schedules.BusStops ) == 0 {
|
2022-09-15 13:38:04 +00:00
|
|
|
qr.Schedules = &map[BusStop] *BusStopBuses{}
|
2022-09-15 12:00:49 +00:00
|
|
|
return &qr
|
2022-09-14 13:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
matches := map[BusStop] *BusStopBuses{}
|
|
|
|
for _, busStop := range *qo.BusStops {
|
|
|
|
|
|
|
|
for _, busStopSch := range schedules.BusStops {
|
|
|
|
if busStopSch.BusStopId == busStop.StationId {
|
|
|
|
matches[busStop] = &busStopSch
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
qr.Schedules = &matches
|
|
|
|
return &qr
|
|
|
|
} else {
|
|
|
|
if len( *qo.BusStops ) == 0 {
|
|
|
|
return &QueryResult{ Error: errors.New( NotFound( qo.SearchTerms ) ) }
|
|
|
|
}
|
|
|
|
|
|
|
|
return &qr
|
2022-09-14 13:21:39 +00:00
|
|
|
}
|
|
|
|
|
2022-09-14 09:12:48 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 13:42:51 +00:00
|
|
|
func test( entry BusStop, val string ) bool {
|
|
|
|
switch true {
|
2022-09-14 14:54:51 +00:00
|
|
|
case strings.Contains( strings.ToUpper( (*entry.Name)["zh"] ), val ):
|
2022-09-13 13:42:51 +00:00
|
|
|
fallthrough
|
2022-09-14 14:54:51 +00:00
|
|
|
case strings.Contains( strings.ToUpper( (*entry.Name)["en"] ), val ):
|
2022-09-13 13:42:51 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
func parse( line string ) ( *QueryObject, error ) {
|
2022-09-13 13:42:51 +00:00
|
|
|
busStops, err := getBusStops()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var route string = ""
|
|
|
|
matches := []BusStop{}
|
|
|
|
|
2022-09-14 09:12:48 +00:00
|
|
|
// Sanitize and assume properties for each of the keywords
|
2022-09-15 12:00:49 +00:00
|
|
|
terms := [] *qTerm{}
|
2022-09-13 13:42:51 +00:00
|
|
|
for _, val := range strings.Split( line, " " ) {
|
2022-09-15 12:00:49 +00:00
|
|
|
|
|
|
|
if val == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-09-14 13:21:39 +00:00
|
|
|
term := qTerm{
|
2022-09-15 12:00:49 +00:00
|
|
|
Org: val,
|
|
|
|
Value: strings.ToUpper( strings.Trim( val, " " ) ),
|
2022-09-13 13:42:51 +00:00
|
|
|
ProblyRoute: strings.ContainsAny( val, utils.ROUTE_CHARS ),
|
|
|
|
}
|
2022-09-15 12:00:49 +00:00
|
|
|
terms = append( terms, &term )
|
2022-09-13 13:42:51 +00:00
|
|
|
}
|
|
|
|
|
2022-09-14 09:12:48 +00:00
|
|
|
// Search for route name first, otherwise search in other props
|
2022-09-13 13:42:51 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
searchTerms := [] *qTerm{}
|
|
|
|
matches_in := []BusStop{}
|
|
|
|
|
|
|
|
for _, term := range terms {
|
|
|
|
if term.ProblyRoute {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
searchTerms = append( searchTerms, term )
|
|
|
|
}
|
2022-09-13 13:42:51 +00:00
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
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
|
2022-09-13 13:42:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-15 12:00:49 +00:00
|
|
|
|
|
|
|
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
|
2022-09-13 13:42:51 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 12:00:49 +00:00
|
|
|
return nil, fmt.Errorf( "Cannot parse: %s", line )
|
2022-09-13 13:42:51 +00:00
|
|
|
}
|