penguin/golifehk

@golifehk bot in Telegram

commit afb73b70ffbd9065fc762a78ae457d671398bd56

author斟酌 鵬兄 <tgckpg@gmail.com>
date2022-09-14T09:12:48Z
subjectGeneralize cache get with expire param
commit afb73b70ffbd9065fc762a78ae457d671398bd56
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2022-09-14T09:12:48Z

    Generalize cache get with expire param
---
 .gitignore                                         |  1 +
 businfo/mtr/busstops_test.go                       | 22 --------
 businfo/mtr/k66.json                               |  0
 .../mtr => datasources/mtr/bus}/busschedule.go     | 56 +++++++++---------
 .../mtr/bus}/busschedule_test.go                   |  2 +-
 {businfo/mtr => datasources/mtr/bus}/busstops.go   | 63 ++++-----------------
 datasources/mtr/bus/busstops_test.go               |  8 +++
 {businfo/mtr => datasources/mtr/bus}/query.go      | 18 +++++-
 {businfo/mtr => datasources/mtr/bus}/query_test.go |  4 +-
 utils/cache.go                                     | 66 ++++++++++++++++++++++
 10 files changed, 130 insertions(+), 110 deletions(-)

diff --git a/.gitignore b/.gitignore
index 343a25c..3c9b48b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 *.csv
 *.json
+*.swp
diff --git a/businfo/mtr/busstops_test.go b/businfo/mtr/busstops_test.go
deleted file mode 100644
index aa81062..0000000
--- a/businfo/mtr/busstops_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package mtr
-
-import (
-    "fmt"
-	"testing"
-)
-
-func TestAll(t *testing.T) {
-    /*
-    entries, err := pullRemote()
-    if err != nil {
-        t.Error( err )
-    }
-    fmt.Println( entries )
-    */
-
-    entries, err := pullLocal()
-    if err != nil {
-        t.Error( err )
-    }
-    fmt.Println( entries )
-}
diff --git a/businfo/mtr/k66.json b/businfo/mtr/k66.json
deleted file mode 100644
index e69de29..0000000
diff --git a/businfo/mtr/busschedule.go b/datasources/mtr/bus/busschedule.go
similarity index 55%
rename from businfo/mtr/busschedule.go
rename to datasources/mtr/bus/busschedule.go
index c796394..2734a3b 100644
--- a/businfo/mtr/busschedule.go
+++ b/datasources/mtr/bus/busschedule.go
@@ -1,12 +1,13 @@
-package mtr
+package bus
 
 import (
-    "fmt"
-    // "byte"
-    // "net/http"
-    "io"
-    "os"
+    "bytes"
     "encoding/json"
+    "io"
+    "net/http"
+    "path/filepath"
+
+    "github.com/tgckpg/golifehk/utils"
 )
 
 type Location struct {
@@ -38,41 +39,38 @@ type BusSchedule struct {
 
 func getSchedule( lang string, routeName string ) ( *BusSchedule, error ) {
 
-    /*
-    values := map[string]string { "language": lang , "routeName": routeName }
-
-    jsonValue, _ := json.Marshal(values)
-
-    resp, err := http.Post(
-        "https://rt.data.gov.hk/v1/transport/mtr/bus/getSchedule",
-        "application/json",
-        bytes.NewBuffer( jsonValue ),
+    CACHE_PATH := filepath.Join(
+        utils.WORKDIR,
+        "mtr_bsch" + "-" + lang + "-" + routeName + ".json",
     )
 
-    if err != nil {
-        return nil, err
+    QUERY_FUNC := func() ( io.ReadCloser, error ) {
+        // Query Remote
+        values := map[string]string { "language": lang , "routeName": routeName }
+        jsonValue, _ := json.Marshal(values)
+        resp, err := http.Post(
+            "https://rt.data.gov.hk/v1/transport/mtr/bus/getSchedule",
+            "application/json",
+            bytes.NewBuffer( jsonValue ),
+        )
+
+        if err != nil {
+            return nil, err
+        }
+
+        return resp.Body, nil
     }
 
-    defer resp.Body.Close()
-
-    data, err := io.ReadAll( resp.Body )
-    /*/
-    f, err := os.Open( "abc.json" )
-    defer f.Close()
-
-    data, err := io.ReadAll( f )
-    //*/
+    buff, err := utils.CacheStream( CACHE_PATH, QUERY_FUNC, 60 )
     if err != nil {
         return nil, err
     }
 
     schedules := BusSchedule{}
-
-    err = json.Unmarshal( data, &schedules )
+    err = json.Unmarshal( buff.Bytes(), &schedules )
     if err != nil {
         return nil, err
     }
 
-    fmt.Printf( "%+v", schedules )
     return &schedules, nil
 }
diff --git a/businfo/mtr/busschedule_test.go b/datasources/mtr/bus/busschedule_test.go
similarity index 92%
rename from businfo/mtr/busschedule_test.go
rename to datasources/mtr/bus/busschedule_test.go
index 9f8ccf6..e48102a 100644
--- a/businfo/mtr/busschedule_test.go
+++ b/datasources/mtr/bus/busschedule_test.go
@@ -1,4 +1,4 @@
-package mtr
+package bus
 
 import (
 	"testing"
diff --git a/businfo/mtr/busstops.go b/datasources/mtr/bus/busstops.go
similarity index 65%
rename from businfo/mtr/busstops.go
rename to datasources/mtr/bus/busstops.go
index a1a05ad..aa807bc 100644
--- a/businfo/mtr/busstops.go
+++ b/datasources/mtr/bus/busstops.go
@@ -1,11 +1,10 @@
-package mtr
+package bus
 
 import (
     "encoding/csv"
     "fmt"
     "io"
     "net/http"
-    "os"
     "path/filepath"
     "strconv"
     "strings"
@@ -26,7 +25,7 @@ type BusStop struct {
 
 var mBusStops *map[string]BusStop
 
-var DEF_CSV string = filepath.Join( utils.WORKDIR, "mtr_bus_stops.csv" )
+var CSV_BUSSTOPS string = filepath.Join( utils.WORKDIR, "mtr_bus_stops.csv" )
 
 func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 
@@ -82,60 +81,18 @@ func readBusStopData( r io.Reader ) ( *map[string]BusStop, error ) {
 
 func getBusStops() (*map[string]BusStop, error) {
 
-    if mBusStops != nil {
-        return mBusStops, nil
-    }
-
-    mBusStops, err := pullLocal()
-    if mBusStops != nil {
-        return mBusStops, err
-    }
-
-    mBusStops, err = pullRemote()
-    if mBusStops != nil {
-        return mBusStops, err
-    }
-
-    return nil, err
-}
-
-func pullRemote() (*map[string]BusStop, error) {
-
-    err := os.MkdirAll( filepath.Dir( DEF_CSV ), 0750 )
-    if err != nil {
-        return nil, err
-    }
-
-    resp, err := http.Get( "https://opendata.mtr.com.hk/data/mtr_bus_stops.csv" )
-    if err != nil {
-        return nil, err
-    }
-
-    defer resp.Body.Close()
-
-    f, err := os.OpenFile( DEF_CSV, os.O_CREATE | os.O_RDWR, 0644 )
-    if err != nil {
-        return nil, err
-    }
-
-    defer f.Close()
-
-    _, err = io.Copy( f, resp.Body )
-    if err != nil {
-        return nil, err
+    QUERY_FUNC := func() ( io.ReadCloser, error ) {
+        resp, err := http.Get( "https://opendata.mtr.com.hk/data/mtr_bus_stops.csv" )
+        if err != nil {
+            return nil, err
+        }
+        return resp.Body, nil
     }
 
-    f.Seek( 0, 0 )
-    return readBusStopData( f )
-}
-
-func pullLocal() (*map[string]BusStop, error) {
-    f, err := os.Open( DEF_CSV )
+    buff, err := utils.CacheStream( CSV_BUSSTOPS, QUERY_FUNC, 7 * 24 * 3600 )
     if err != nil {
         return nil, err
     }
 
-    defer f.Close()
-
-    return readBusStopData( f )
+    return readBusStopData( buff )
 }
diff --git a/datasources/mtr/bus/busstops_test.go b/datasources/mtr/bus/busstops_test.go
new file mode 100644
index 0000000..5d3c382
--- /dev/null
+++ b/datasources/mtr/bus/busstops_test.go
@@ -0,0 +1,8 @@
+package bus
+
+import (
+	"testing"
+)
+
+func TestAll(t *testing.T) {
+}
diff --git a/businfo/mtr/query.go b/datasources/mtr/bus/query.go
similarity index 82%
rename from businfo/mtr/query.go
rename to datasources/mtr/bus/query.go
index e0e61d6..c2122e3 100644
--- a/businfo/mtr/query.go
+++ b/datasources/mtr/bus/query.go
@@ -1,4 +1,4 @@
-package mtr
+package bus
 
 import (
     "fmt"
@@ -8,7 +8,12 @@ import (
 
 type QueryObject struct {
     Route string
+    BusStops *[]BusStop
+}
+
+type QueryResult struct {
     BusStops []BusStop
+    err string
 }
 
 type Term struct {
@@ -16,6 +21,10 @@ type Term struct {
     ProblyRoute bool
 }
 
+func Query( message string ) *QueryResult {
+    return &QueryResult{ err: "No Result" }
+}
+
 func test( entry BusStop, val string ) bool {
     switch true {
     case strings.Contains( entry.Name_zhant, val ):
@@ -37,6 +46,7 @@ func parse( line string ) ( *QueryObject, error ) {
     var searches = []string{}
     matches := []BusStop{}
 
+    // Sanitize and assume properties for each of the keywords
     terms := []Term{}
     for _, val := range strings.Split( line, " " ) {
         val = strings.ToUpper( strings.Trim( val, " " ) )
@@ -47,6 +57,7 @@ func parse( line string ) ( *QueryObject, error ) {
         terms = append( terms, term )
     }
 
+    // Search for route name first, otherwise search in other props
     for _, entry := range *busStops {
 
         // Search for RouteId
@@ -69,6 +80,8 @@ func parse( line string ) ( *QueryObject, error ) {
         }
     }
 
+    // If route found, filter out all other route
+    // then search within that route
     if route != "" && 0 < len( searches ) {
         matches_in := []BusStop{}
         for _, entry := range matches {
@@ -86,6 +99,5 @@ func parse( line string ) ( *QueryObject, error ) {
         matches = matches_in
     }
 
-
-    return &QueryObject{ Route: route, BusStops: matches }, err
+    return &QueryObject{ Route: route, BusStops: &matches }, err
 }
diff --git a/businfo/mtr/query_test.go b/datasources/mtr/bus/query_test.go
similarity index 80%
rename from businfo/mtr/query_test.go
rename to datasources/mtr/bus/query_test.go
index db0feb0..131c852 100644
--- a/businfo/mtr/query_test.go
+++ b/datasources/mtr/bus/query_test.go
@@ -1,4 +1,4 @@
-package mtr
+package bus
 
 import (
     "fmt"
@@ -11,6 +11,6 @@ func TestQuerySchedule(t *testing.T) {
         t.Error( err )
     }
 
-    fmt.Println( qo )
+    fmt.Printf( "%+v", qo )
 }
 
diff --git a/utils/cache.go b/utils/cache.go
new file mode 100644
index 0000000..00540fc
--- /dev/null
+++ b/utils/cache.go
@@ -0,0 +1,66 @@
+package utils
+
+import (
+    "bytes"
+    "io"
+    "os"
+    "path/filepath"
+    "time"
+)
+
+
+func CacheStream( path string, readStream func() ( io.ReadCloser, error ), expires int ) ( *bytes.Buffer, error ) {
+
+    cache, err := os.Stat( path )
+
+    // Check if cache exists and not expired
+    if err == nil {
+        expired := cache.ModTime().Add( 60 * 1e9 )
+        if time.Now().Before( expired ) {
+            f, err := os.Open( path )
+            if err == nil {
+                defer f.Close()
+                writeBuff := bytes.NewBuffer( []byte{} )
+                _, err = io.Copy( writeBuff, f )
+                if err == nil {
+                    return writeBuff, nil
+                }
+            }
+        }
+    }
+
+    err = os.MkdirAll( filepath.Dir( path ), 0750 )
+    if err != nil {
+        return nil, err
+    }
+
+    writeBuff := bytes.NewBuffer( []byte{} )
+
+    // Get the reader that return new data
+    s, err := readStream()
+    if err != nil {
+        return nil, err
+    }
+
+    defer s.Close()
+
+    _, err = io.Copy( writeBuff, s )
+    if err != nil {
+        return nil, err
+    }
+
+    f, err := os.OpenFile( path, os.O_CREATE | os.O_WRONLY | os.O_TRUNC, 0644 )
+    if err != nil {
+        return nil, err
+    }
+
+    defer f.Close()
+
+    data := writeBuff.Bytes()
+    _, err = io.Copy( f, bytes.NewReader( data ) )
+    if err != nil {
+        return nil, err
+    }
+
+    return writeBuff, nil
+}