-
-
Save flyfire/10021448 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package weather | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "html/template" | |
| "io/ioutil" | |
| "log" | |
| "net/http" | |
| "net/url" | |
| "sync" | |
| "appengine" | |
| "appengine/urlfetch" | |
| ) | |
| type DataPoint struct { | |
| Time float64 | |
| Summary string | |
| Icon string | |
| SunriseTime float64 | |
| SunsetTime float64 | |
| PrecipIntensity float64 | |
| PrecipIntensityMax float64 | |
| PrecipIntensityMaxTime float64 | |
| PrecipProbability float64 | |
| PrecipType string | |
| PrecipAccumulation float64 | |
| Temperature float64 | |
| TemperatureMin float64 | |
| TemperatureMinTime float64 | |
| TemperatureMax float64 | |
| TemperatureMaxTime float64 | |
| DewPoint float64 | |
| WindSpeed float64 | |
| WindBearing float64 | |
| CloudCover float64 | |
| Humidity float64 | |
| Pressure float64 | |
| Visibility float64 | |
| Ozone float64 | |
| } | |
| type Forecast struct { | |
| Latitude float64 | |
| Longitude float64 | |
| Timezone string | |
| Offset float64 | |
| Currently DataPoint | |
| Junk string | |
| } | |
| func init() { | |
| http.Handle("/stylesheets/", http.StripPrefix("/stylesheets/", http.FileServer(http.Dir("stylesheets")))) | |
| http.HandleFunc("/", handler) | |
| http.HandleFunc("/display", display) | |
| } | |
| func handler(w http.ResponseWriter, r *http.Request) { | |
| fmt.Fprint(w, rootForm) | |
| } | |
| const rootForm = ` | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <!-- Basic Page Needs | |
| ================================================== --> | |
| <meta charset="utf-8"> | |
| <title>Weather Forecast</title> | |
| <meta name="description" content="Weather Forecast"> | |
| <meta name="author" content="Satish Talim"> | |
| <!-- Mobile Specific Metas | |
| ================================================== --> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> | |
| <!-- CSS | |
| ================================================== --> | |
| <link rel="stylesheet" href="stylesheets/base.css"> | |
| <link rel="stylesheet" href="stylesheets/skeleton.css"> | |
| <link rel="stylesheet" href="stylesheets/layout.css"> | |
| <link rel="stylesheet" href="stylesheets/table.css"> | |
| <!-- Favicons | |
| ================================================== --> | |
| <link rel="shortcut icon" href="images/favicon.ico"> | |
| <link rel="apple-touch-icon" href="images/apple-touch-icon.png"> | |
| <link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png"> | |
| <link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png"> | |
| <!-- Validation | |
| ================================================== --> | |
| <script> | |
| function validateForm() | |
| { | |
| var c1=document.forms["myForm"]["city1"].value; | |
| var c2=document.forms["myForm"]["city2"].value; | |
| var c3=document.forms["myForm"]["city3"].value; | |
| var c4=document.forms["myForm"]["city4"].value; | |
| if ((c1==null || c1=="") || (c2==null || c2=="") || | |
| (c3==null || c3=="") || (c4==null || c4=="")) | |
| { | |
| alert("City Name must be filled out"); | |
| return false; | |
| } | |
| } | |
| </script> | |
| </head> | |
| <body> | |
| <!-- Primary Page Layout | |
| ================================================== --> | |
| <div class="container"> | |
| <div class="sixteen columns"> | |
| <h1 class="remove-bottom" style="margin-top: 40px">Weather Forecast</h1> | |
| <h5>Version 1.0</h5> | |
| <hr /> | |
| </div> | |
| <div class="sixteen columns"> | |
| <p>Please enter the cities for which you want a weather forecast.</p> | |
| <form name ="myForm" action="/display" onsubmit="return validateForm()" method="post" accept-charset="utf-8"> | |
| <!-- Label and text input --> | |
| <label for="regularInput1">City Name 1</label> | |
| <input type="text" name="city1" id="regularInput1" /> | |
| <label for="regularInput2">City Name 2</label> | |
| <input type="text" name="city2" id="regularInput2" /> | |
| <label for="regularInput3">City Name 3</label> | |
| <input type="text" name="city3" id="regularInput3" /> | |
| <label for="regularInput4">City Name 4</label> | |
| <input type="text" name="city4" id="regularInput4" /> | |
| <button type="submit">Submit Form</button> | |
| </form> | |
| </div> | |
| </div><!-- container --> | |
| <!-- End Document | |
| ================================================== --> | |
| </body> | |
| </html> | |
| ` | |
| var displayTemplate = template.Must(template.New("display").Parse(displayTemplateHTML)) | |
| func display(w http.ResponseWriter, r *http.Request) { | |
| addr := []string{r.FormValue("city1"), r.FormValue("city2"), r.FormValue("city3"), r.FormValue("city4")} | |
| f := make([]Forecast, 4, 4) | |
| // Create a wait group to manage the goroutines | |
| var waitGroup sync.WaitGroup | |
| // Perform 4 concurrent queries | |
| waitGroup.Add(4) | |
| // Perform 4 concurrent queries | |
| for query := 0; query < 4; query++ { | |
| go Get(r, query, &waitGroup, addr, f) | |
| } | |
| // Wait for all the queries to complete. | |
| waitGroup.Wait() | |
| displayTemplate.Execute(w, f) | |
| } | |
| // Get is a function that is launched as a goroutine | |
| func Get(r *http.Request, query int, waitGroup *sync.WaitGroup, addr []string, f []Forecast) { | |
| // Decrement the wait group count so the program knows this | |
| // has been completed once the goroutine exits. | |
| defer waitGroup.Done() | |
| // Geocoding API | |
| // QueryEscape escapes the addr string so | |
| // it can be safely placed inside a URL query | |
| // safeAddr := url.QueryEscape(addr) | |
| safeAddr := url.QueryEscape(addr[query]) | |
| fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=%s", safeAddr) | |
| // App engine stuff starts | |
| c := appengine.NewContext(r) | |
| client := urlfetch.Client(c) | |
| // Build the request | |
| resp, err := client.Get(fullUrl) | |
| if err != nil { | |
| panic(err) | |
| } | |
| // Callers should close resp.Body | |
| // when done reading from it | |
| // Defer the closing of the body | |
| defer resp.Body.Close() | |
| // App engine stuff ends | |
| // Read the content into a byte array | |
| body, dataReadErr := ioutil.ReadAll(resp.Body) | |
| if dataReadErr != nil { | |
| panic(dataReadErr) | |
| } | |
| res := make(map[string][]map[string]map[string]map[string]interface{}, 0) | |
| // We will be using the Unmarshal function | |
| // to transform our JSON bytes into the | |
| // appropriate structure. | |
| // The Unmarshal function accepts a byte array | |
| // and a reference to the object which shall be | |
| // filled with the JSON data (this is simplifying, | |
| // it actually accepts an interface) | |
| json.Unmarshal(body, &res) | |
| // lat, lng as float64 | |
| lat, _ := res["results"][0]["geometry"]["location"]["lat"] | |
| lng, _ := res["results"][0]["geometry"]["location"]["lng"] | |
| // Forecast API | |
| // %.13f is used to convert float64 to a string | |
| url := fmt.Sprintf("https://api.forecast.io/forecast/yourapikey/%.13f,%.13f?units=ca", lat, lng) | |
| // App engine stuff starts | |
| c1 := appengine.NewContext(r) | |
| client1 := urlfetch.Client(c1) | |
| // Build the request | |
| resp1, err1 := client1.Get(url) | |
| if err1 != nil { | |
| panic(err1) | |
| } | |
| // Callers should close resp1.Body | |
| // when done reading from it | |
| // Defer the closing of the body | |
| defer resp1.Body.Close() | |
| // App engine stuff ends | |
| fbody, err2 := ioutil.ReadAll(resp1.Body) | |
| if err2 != nil { | |
| log.Fatal(err2) | |
| } | |
| json.Unmarshal(fbody, &f[query]) | |
| } | |
| const displayTemplateHTML = ` | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <!-- Basic Page Needs | |
| ================================================== --> | |
| <meta charset="utf-8"> | |
| <title>Weather Forecast</title> | |
| <meta name="description" content="Weather Forecast"> | |
| <meta name="author" content="Satish Talim"> | |
| <!-- Mobile Specific Metas | |
| ================================================== --> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> | |
| <!-- CSS | |
| ================================================== --> | |
| <link rel="stylesheet" href="stylesheets/base.css"> | |
| <link rel="stylesheet" href="stylesheets/skeleton.css"> | |
| <link rel="stylesheet" href="stylesheets/layout.css"> | |
| <link rel="stylesheet" href="stylesheets/table.css"> | |
| <!-- Favicons | |
| ================================================== --> | |
| <link rel="shortcut icon" href="images/favicon.ico"> | |
| <link rel="apple-touch-icon" href="images/apple-touch-icon.png"> | |
| <link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png"> | |
| <link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png"> | |
| </head> | |
| <body> | |
| <!-- Primary Page Layout | |
| ================================================== --> | |
| <div class="container"> | |
| <div class="sixteen columns"> | |
| <h1 class="remove-bottom" style="margin-top: 40px">Weather Forecast</h1> | |
| <h5>Version 1.0</h5> | |
| <hr /> | |
| </div> | |
| <div class="sixteen columns"> | |
| <p>The table below provides current weather forecast for the cities you entered.</p> | |
| <div class="example"> | |
| <!-- https://github.com/dstotijn/Skeleton-tables --> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Timezone</th> | |
| <th>Temperature</th> | |
| <th>Summary</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {{range .}} | |
| <tr> | |
| <td>{{.Timezone}}</td> | |
| <td>{{.Currently.Temperature}}</td> | |
| <td>{{.Currently.Summary}}</td> | |
| </tr> | |
| {{ end }} | |
| </tbody> | |
| </table> | |
| </div> | |
| <p><a href="/">Start again!</a></p> | |
| </div> | |
| </div><!-- container --> | |
| <!-- End Document | |
| ================================================== --> | |
| </body> | |
| </html> | |
| ` | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment