penguin/golifehk

@golifehk bot in Telegram

commit 534bc70f0f9aaace3496e122b112e50f6e8fede7

author斟酌 鵬兄 <tgckpg@gmail.com>
date2022-09-14T13:21:39Z
subjectPartially working draft
commit 534bc70f0f9aaace3496e122b112e50f6e8fede7
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2022-09-14T13:21:39Z

    Partially working draft
---
 datasources/mtr/bus/BusStop.go     | 27 ++++++++++++++++++
 datasources/mtr/bus/QueryResult.go | 48 ++++++++++++++++++++++++++++++++
 datasources/mtr/bus/busschedule.go |  5 ++--
 datasources/mtr/bus/busstops.go    | 44 ++++++++++++++++++-----------
 datasources/mtr/bus/query.go       | 57 ++++++++++++++++++++++++++++----------
 datasources/mtr/bus/query_test.go  | 10 ++-----
 6 files changed, 151 insertions(+), 40 deletions(-)

diff --git a/datasources/mtr/bus/BusStop.go b/datasources/mtr/bus/BusStop.go
new file mode 100644
index 0000000..abec4a0
--- /dev/null
+++ b/datasources/mtr/bus/BusStop.go
@@ -0,0 +1,27 @@
+package bus
+
+type BusStop struct {
+    RouteId string
+    Direction string
+    StationSeq int
+    StationId string
+    Latitude float64
+    Longtitude float64
+
+    Name *map[string] string
+    RouteStops *map[int] *BusStop
+}
+
+func ( busStop BusStop ) PrevStop() *BusStop {
+    if v, hasKey := (*busStop.RouteStops)[ busStop.StationSeq - 1 ]; hasKey {
+        return v
+    }
+    return nil
+}
+
+func ( busStop BusStop ) NextStop() *BusStop {
+    if v, hasKey := (*busStop.RouteStops)[ busStop.StationSeq + 1 ]; hasKey {
+        return v
+    }
+    return nil
+}
diff --git a/datasources/mtr/bus/QueryResult.go b/datasources/mtr/bus/QueryResult.go
new file mode 100644
index 0000000..90b995c
--- /dev/null
+++ b/datasources/mtr/bus/QueryResult.go
@@ -0,0 +1,48 @@
+package bus
+
+import (
+    "fmt"
+    "strings"
+)
+
+type QueryResult struct {
+    Schedules *map[BusStop] *BusStopBuses
+    Lang string
+    Error error
+}
+
+func ( result QueryResult ) Message() string {
+
+    if result.Error != nil {
+        return fmt.Sprintf( "%s", result.Error )
+    }
+
+    sb := strings.Builder{}
+
+    for busStop, buses := range *result.Schedules {
+
+        if busStop.PrevStop() != nil {
+            sb.WriteString( (*busStop.PrevStop().Name)[ result.Lang ] )
+            sb.WriteString( " > *" )
+        }
+
+        sb.WriteString( (*busStop.Name)[ result.Lang ] )
+        sb.WriteString( "*" )
+
+        if busStop.NextStop() != nil {
+            sb.WriteString( " > " )
+            sb.WriteString( (*busStop.NextStop().Name)[ result.Lang ] )
+        }
+
+        sb.WriteString( "\n" )
+        for _, bus := range buses.Buses {
+            sb.WriteString( "  * " )
+            sb.WriteString( bus.ETAText )
+            sb.WriteString( "\n" )
+        }
+
+        sb.WriteString( "\n" )
+    }
+
+    return sb.String()
+}
diff --git a/datasources/mtr/bus/busschedule.go b/datasources/mtr/bus/busschedule.go
index 2734a3b..f70676d 100644
--- a/datasources/mtr/bus/busschedule.go
+++ b/datasources/mtr/bus/busschedule.go
@@ -27,14 +27,15 @@ type Bus struct {
     LineRef string `json:"lineRef"`
 }
 
-type BusStopSchedules struct {
+type BusStopBuses struct {
     Buses [] Bus `json:"bus"`
+    BusStopId string `json:"busStopId"`
     Suspended string `json:"isSuspended"`
 }
 
 type BusSchedule struct {
     RefreshTime int `json:"appRefreshTimeInSecond,string"`
-    BusStops [] BusStopSchedules `json:"busStop"`
+    BusStops [] BusStopBuses `json:"busStop"`
 }
 
 func getSchedule( lang string, routeName string ) ( *BusSchedule, error ) {
diff --git a/datasources/mtr/bus/busstops.go b/datasources/mtr/bus/busstops.go
index aa807bc..ee88fec 100644
--- a/datasources/mtr/bus/busstops.go
+++ b/datasources/mtr/bus/busstops.go
@@ -12,19 +12,6 @@ import (
     "github.com/tgckpg/golifehk/utils"
 )
 
-type BusStop struct {
-    RouteId string
-    Direction string
-    StationSeq string
-    StationId string
-    Latitude float64
-    Longtitude float64
-    Name_zhant string
-    Name_en string
-}
-
-var mBusStops *map[string]BusStop
-
 var CSV_BUSSTOPS string = filepath.Join( utils.WORKDIR, "mtr_bus_stops.csv" )
 
 func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
@@ -36,6 +23,8 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
     }
 
     busStops := map[string]BusStop{}
+    routeStops := map[string] map[string] map[int] *BusStop{}
+
     var headers []string
     for i, line := range entries {
         if i == 0 {
@@ -45,6 +34,8 @@ 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] {
                 case "ROUTE_ID":
@@ -52,7 +43,8 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
                 case "DIRECTION":
                     entry.Direction = value
                 case "STATION_SEQNO":
-                    entry.StationSeq = value
+                    v, _ := strconv.Atoi( value )
+                    entry.StationSeq = v
                 case "STATION_ID":
                     entry.StationId = value
                 case "STATION_LATITUDE":
@@ -62,9 +54,9 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
                     v, _ := strconv.ParseFloat( value, 64 )
                     entry.Longtitude = v
                 case "STATION_NAME_CHI":
-                    entry.Name_zhant = value
+                    i18n_Name["zh"] = value
                 case "STATION_NAME_ENG":
-                    entry.Name_en = value
+                    i18n_Name["en"] = value
                 default:
                     return nil, fmt.Errorf( "Unknown header \"%s\"", headers[j] )
             }
@@ -74,6 +66,26 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
             return nil, fmt.Errorf( "Duplicated entry %+v", entry )
         }
 
+        routeDir, hasKey := routeStops[ entry.RouteId ]
+        if !hasKey {
+            routeStops[ entry.RouteId ] = map[string] map[int] *BusStop{}
+            routeDir = routeStops[ entry.RouteId ]
+        }
+
+        route, hasKey := routeDir[ entry.Direction ]
+        if !hasKey {
+            routeDir[ entry.Direction ] = map[int] *BusStop{}
+            route = routeDir[ entry.Direction ]
+        }
+
+        _, hasKey = route[ entry.StationSeq ]
+        if !hasKey {
+            route[ entry.StationSeq ] = &entry
+        }
+
+        entry.Name = &i18n_Name
+        entry.RouteStops = &route
+
         busStops[ entry.StationId ] = entry
     }
     return &busStops, nil
diff --git a/datasources/mtr/bus/query.go b/datasources/mtr/bus/query.go
index c2122e3..0d79081 100644
--- a/datasources/mtr/bus/query.go
+++ b/datasources/mtr/bus/query.go
@@ -1,41 +1,68 @@
 package bus
 
 import (
+    "errors"
     "fmt"
     "strings"
     "github.com/tgckpg/golifehk/utils"
 )
 
-type QueryObject struct {
+type queryObj struct {
     Route string
     BusStops *[]BusStop
 }
 
-type QueryResult struct {
-    BusStops []BusStop
-    err string
-}
-
-type Term struct {
+type qTerm struct {
     Value string
     ProblyRoute bool
 }
 
-func Query( message string ) *QueryResult {
-    return &QueryResult{ err: "No Result" }
+func Query( lang string, message string ) *QueryResult {
+
+    qo, err := parse( message )
+    if err != nil {
+        return &QueryResult{ Error: err }
+    }
+
+    if qo.Route != "" {
+        schedules, err := getSchedule( lang, qo.Route )
+        if err != nil {
+            return &QueryResult{ Error: err }
+        }
+
+        if len( schedules.BusStops ) == 0 {
+            return &QueryResult{ Error: errors.New( "Schedules are empty. Maybe outside service time?" ) }
+        }
+
+        matches := map[BusStop] *BusStopBuses{}
+        for _, busStop := range *qo.BusStops {
+
+            for _, busStopSch := range schedules.BusStops {
+                if busStopSch.BusStopId == busStop.StationId {
+                    matches[busStop] = &busStopSch
+                    break
+                }
+            }
+
+        }
+
+        return &QueryResult{ Lang: lang, Schedules: &matches }
+    }
+
+    return &QueryResult{ Error: errors.New( "No Result" ) }
 }
 
 func test( entry BusStop, val string ) bool {
     switch true {
-    case strings.Contains( entry.Name_zhant, val ):
+    case strings.Contains( (*entry.Name)["zh"], val ):
         fallthrough
-    case strings.Contains( entry.Name_en, val ):
+    case strings.Contains( (*entry.Name)["en"], val ):
         return true
     }
     return false
 }
 
-func parse( line string ) ( *QueryObject, error ) {
+func parse( line string ) ( *queryObj, error ) {
     busStops, err := getBusStops()
 
     if err != nil {
@@ -47,10 +74,10 @@ func parse( line string ) ( *QueryObject, error ) {
     matches := []BusStop{}
 
     // Sanitize and assume properties for each of the keywords
-    terms := []Term{}
+    terms := []qTerm{}
     for _, val := range strings.Split( line, " " ) {
         val = strings.ToUpper( strings.Trim( val, " " ) )
-        term := Term{
+        term := qTerm{
             Value: val,
             ProblyRoute: strings.ContainsAny( val, utils.ROUTE_CHARS ),
         }
@@ -99,5 +126,5 @@ func parse( line string ) ( *QueryObject, error ) {
         matches = matches_in
     }
 
-    return &QueryObject{ Route: route, BusStops: &matches }, err
+    return &queryObj{ Route: route, BusStops: &matches }, err
 }
diff --git a/datasources/mtr/bus/query_test.go b/datasources/mtr/bus/query_test.go
index 131c852..cbea0d1 100644
--- a/datasources/mtr/bus/query_test.go
+++ b/datasources/mtr/bus/query_test.go
@@ -5,12 +5,8 @@ import (
 	"testing"
 )
 
-func TestQuerySchedule(t *testing.T) {
-    qo, err := parse( "K74 天瑞" )
-    if err != nil {
-        t.Error( err )
-    }
+func TestQuerySchedule( t *testing.T ) {
+    qo := Query( "zh", "K73 池" )
 
-    fmt.Printf( "%+v", qo )
+    fmt.Print( qo.Message() )
 }
-