diff --git a/.gitignore b/.gitignore index ba2acb9..cfc5d29 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ go.work # Partner project SimplrWeather/ + +# Protobuf definitions +proto/ \ No newline at end of file diff --git a/SimplrWeather b/SimplrWeather index 8c4b035..da7e324 160000 --- a/SimplrWeather +++ b/SimplrWeather @@ -1 +1 @@ -Subproject commit 8c4b03510c4a3b89e1adb28d5ee36ad091240f0a +Subproject commit da7e32450045132e40ab6c09ff2dbc199f450f9d diff --git a/cmd/advanced.go b/cmd/advanced.go index abc4b35..6fcbc03 100644 --- a/cmd/advanced.go +++ b/cmd/advanced.go @@ -8,7 +8,7 @@ import ( "strings" ) -func advancedMenu(app *application) { +func advancedMenu(app *Application) { var option string @@ -19,6 +19,7 @@ func advancedMenu(app *application) { fmt.Printf("1. Change units (Default: imperial; Current: %s)\n", app.Config.Units) fmt.Println("2. Enter precise location") + fmt.Println("3. Get historical data") fmt.Print("0. Back\n\n") // Read user input @@ -27,7 +28,6 @@ func advancedMenu(app *application) { if err != nil { log.Println(err) } - option = strings.TrimSuffix(input, "\n") // Check user input @@ -54,7 +54,23 @@ func advancedMenu(app *application) { fmt.Println("Are you sure the coordinates are valid?") } } + printWeather(app) + app.Forecast.Main.Temp = 0.00 + } else if option == "3" { + var validLoc bool + for !validLoc { + getPreciseLocation(app) + getDate(app) + getHistoricalData(app.Client, app) + + if app.Forecast.Main.Temp != 0.00 { + validLoc = true + } else { + fmt.Println("I couldn't get any information for that location.") + fmt.Println("Are you sure the coordinates are valid?") + } + } printWeather(app) app.Forecast.Main.Temp = 0.00 } diff --git a/cmd/history.go b/cmd/history.go new file mode 100644 index 0000000..937e7b5 --- /dev/null +++ b/cmd/history.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + + pb "codeberg.org/andcscott/weather-cli/proto" +) + +// Get a date from the user and save it to the config +func getDate(app *Application) { + + fmt.Print("Enter 4-digit year: ") + _, err := fmt.Scanf("%d", &app.Config.Date.Year) + if err != nil { + fmt.Println("Invalid year") + } + + fmt.Print("Enter month (1-12): ") + _, err = fmt.Scanf("%d", &app.Config.Date.Month) + if err != nil { + fmt.Println("Invalid year") + } + + fmt.Print("Enter day (1-31): ") + _, err = fmt.Scanf("%d", &app.Config.Date.Day) + if err != nil { + fmt.Println("Invalid year") + } +} + +// Query SimplrWeather for date in Unix time +func getUnixTime(c pb.RouteGuideClient) int32 { + + var year, month, day int32 + + res, err := c.GetUnixTime(context.Background(), &pb.Date{ + Year: year, + Month: month, + Day: day, + }) + if err != nil { + fmt.Printf("Error getting Unix time: %v\n", err) + } + return res.Unixtime +} + +// Query SimplrWeather for historical data +func getHistoricalData(c pb.RouteGuideClient, app *Application) { + + var lat, lon int + lat, _ = strconv.Atoi(app.Config.Latitude) + lon, _ = strconv.Atoi(app.Config.Longitude) + + res, err := c.GetHistoricalData(context.Background(), &pb.LocationDate{ + Latitude: int32(lat), + Longitude: int32(lon), + Year: app.Config.Date.Year, + Month: app.Config.Date.Month, + Day: app.Config.Date.Day, + }) + if err != nil { + fmt.Printf("Error getting historical data: %v", err) + } + + err = json.Unmarshal([]byte(res.Data), &app.Forecast) + if err != nil { + fmt.Printf("Error reading data from server: %v", err) + } +} diff --git a/cmd/location.go b/cmd/location.go index bee23a0..e889439 100644 --- a/cmd/location.go +++ b/cmd/location.go @@ -12,7 +12,7 @@ import ( ) // Use WAN address to obtain location data -func getLocation(app *application) { +func getLocation(app *Application) { res, err := http.Get("https://ipinfo.io/json") if err != nil { @@ -37,7 +37,7 @@ func getLocation(app *application) { } // Prompt user for location -func getPreciseLocation(app *application) { +func getPreciseLocation(app *Application) { fmt.Print("\nEnter latitude: ") diff --git a/cmd/main.go b/cmd/main.go index 4b2319f..e2cdd90 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,18 +1,18 @@ package main import ( - "bufio" - "fmt" "log" "os" - "strings" + pb "codeberg.org/andcscott/weather-cli/proto" "github.com/joho/godotenv" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) const version string = "0.2.0" -type weatherMain struct { +type Weather struct { Temp float32 `json:"temp"` FeelsLike float32 `json:"feels_like"` HighTemp float32 `json:"temp_max"` @@ -21,93 +21,68 @@ type weatherMain struct { Humidity uint `json:"humidity"` } -type weatherWind struct { +type Wind struct { Speed float32 `json:"speed"` Gust float32 `json:"gust"` } -type forecast struct { +type Forecast struct { //Overview weatherOverview `json:"weather"` - Main weatherMain `json:"main"` - Wind weatherWind `json:"wind"` + Main Weather `json:"main"` + Wind Wind `json:"wind"` } -type config struct { +type Date struct { + Year int32 + Month int32 + Day int32 +} + +type Config struct { Units string `json:"units"` Location string `json:"loc"` Longitude string `json:"lat"` Latitude string `json:"lon"` + Date Date ApiKey string `json:"appid"` } -type application struct { - Forecast forecast - Config config +type Application struct { + Forecast Forecast + Config Config Version string + Client pb.RouteGuideClient } func main() { + conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + // Load .env - err := godotenv.Load() + err = godotenv.Load() if err != nil { log.Fatalln(err) } // Read API_KEY from .env and create app - cfg := config{ + cfg := Config{ Units: "imperial", ApiKey: os.Getenv("API_KEY"), } - fcst := forecast{} + fcst := Forecast{} - app := application{ + app := Application{ Config: cfg, Forecast: fcst, Version: version, + Client: pb.NewRouteGuideClient(conn), } - // Display main menu - fmt.Println("\n=====================================================") - fmt.Printf("| Welcome to the OpenWeatherMap-gRPC Client! v%s |\n", app.Version) - fmt.Println("=====================================================") - - fmt.Printf("New in version %s:\n", app.Version) - fmt.Println(" - Advanced option: Get historical data back to 1972") - fmt.Println("New in version 0.1.0") - fmt.Println(" - Default option: Automatically determine location") - fmt.Println(" - Advanced option: Enter exact location for anywhere in the world") - fmt.Println(" - Advanced option: Change back and forth between imperial and metric units of measurement") - - var option string - // Menu loop - for option != "0" { - fmt.Print("\nMain Menu\n---------\n\n") - fmt.Println("1. Today's forecast (use current location, default)") - fmt.Println("2. Advanced options (Change units, precise location, etc.)") - fmt.Print("0. Exit\n\n") - - // Read user input - reader := bufio.NewReader(os.Stdin) - input, err := reader.ReadString('\n') - if err != nil { - log.Println(err) - } - option = strings.TrimSuffix(input, "\n") - - // Check user input - if option == "1" || option == "" { - getLocation(&app) - getCurrent(&app) - printWeather(&app) - } else if option == "2" { - advancedMenu(&app) - } else if option == "0" { - return - } else { - fmt.Print("\nOops! An error occurred, please choose a valid option.\n\n") - } - } + mainMenu(&app) } diff --git a/cmd/menu.go b/cmd/menu.go new file mode 100644 index 0000000..5ef0455 --- /dev/null +++ b/cmd/menu.go @@ -0,0 +1,55 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "strings" +) + +func mainMenu(app *Application) { + + // Display main menu + fmt.Println("\n=====================================================") + fmt.Printf("| Welcome to the OpenWeatherMap-gRPC Client! v%s |\n", app.Version) + fmt.Println("=====================================================") + + fmt.Printf("New in version %s:\n", app.Version) + fmt.Println(" - Advanced option: Get historical data back to 1972") + fmt.Println("New in version 0.1.0") + fmt.Println(" - Default option: Automatically determine location") + fmt.Println(" - Advanced option: Enter exact location for anywhere in the world") + fmt.Println(" - Advanced option: Change back and forth between imperial and metric units of measurement") + + var option string + // Menu loop + for option != "0" { + fmt.Print("\nMain Menu\n---------\n\n") + fmt.Println("1. Today's forecast (use current location, default)") + fmt.Println("2. Advanced options (Change units, precise location, etc.)") + fmt.Print("0. Exit\n\n") + + // Read user input + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + log.Println(err) + } + option = strings.TrimSuffix(input, "\n") + + // Check user input + if option == "1" || option == "" { + getLocation(app) + getCurrent(app) + printWeather(app) + } else if option == "2" { + advancedMenu(app) + } else if option == "0" { + return + } else { + fmt.Print("\nOops! An error occurred, please choose a valid option.\n\n") + } + } + +} diff --git a/cmd/render.go b/cmd/render.go index a17e85f..be36e0e 100644 --- a/cmd/render.go +++ b/cmd/render.go @@ -3,7 +3,7 @@ package main import "fmt" // Prints saved weather data to the terminal -func printWeather(app *application) { +func printWeather(app *Application) { var unitStr string if app.Config.Units == "imperial" { diff --git a/cmd/weather.go b/cmd/weather.go index 8e9dab4..71491c7 100644 --- a/cmd/weather.go +++ b/cmd/weather.go @@ -8,7 +8,7 @@ import ( ) // Get and store the current forecast -func getCurrent(app *application) { +func getCurrent(app *Application) { lat := "lat=" + app.Config.Latitude lon := "&lon=" + app.Config.Longitude