├── .babelrc ├── .gitignore ├── README.md ├── api-config.json ├── google-places.go ├── index.html ├── main.go ├── package.json ├── serve.go ├── src ├── App.vue ├── components │ ├── BusinessBox.vue │ ├── LoadingIcon.vue │ ├── NavBar.vue │ ├── ReviewBox.vue │ └── YelpStars.vue ├── main.js └── pages │ └── SearchForm.vue ├── static └── img │ └── yelp_stars │ ├── small_0.png │ ├── small_0@2x.png │ ├── small_0@3x.png │ ├── small_1.png │ ├── small_1@2x.png │ ├── small_1@3x.png │ ├── small_1_half.png │ ├── small_1_half@2x.png │ ├── small_1_half@3x.png │ ├── small_2.png │ ├── small_2@2x.png │ ├── small_2@3x.png │ ├── small_2_half.png │ ├── small_2_half@2x.png │ ├── small_2_half@3x.png │ ├── small_3.png │ ├── small_3@2x.png │ ├── small_3@3x.png │ ├── small_3_half.png │ ├── small_3_half@2x.png │ ├── small_3_half@3x.png │ ├── small_4.png │ ├── small_4@2x.png │ ├── small_4@3x.png │ ├── small_4_half.png │ ├── small_4_half@2x.png │ ├── small_4_half@3x.png │ ├── small_5.png │ ├── small_5@2x.png │ └── small_5@3x.png ├── text-process.go ├── webpack.config.js └── yelp.go /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["latest", { 4 | "es2015": { "modules": false } 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goex-search 2 | 3 | A Yelp search app that summarizes reviews using Watson and Aylien Text API 4 | 5 | [Demo](http://ter.ps/goex) 6 | 7 | ## Install 8 | 9 | Install [golang](https://golang.org/doc/install) and set your [gopath](https://github.com/golang/go/wiki/GOPATH). 10 | 11 | Get api keys for: 12 | - [yelp fusion](https://www.yelp.com/developers) 13 | - [google places (web service)](https://developers.google.com/places/web-service/) 14 | - [watson language understanding](https://www.ibm.com/watson/developercloud/natural-language-understanding.html) 15 | - [aylien text analysis](http://aylien.com/text-api) 16 | 17 | ``` bash 18 | cd $GOPATH/src/ 19 | git clone https://github.com/johannkm/goex-search.git 20 | 21 | cd goex-search/ 22 | ``` 23 | 24 | Set your api credentials in `api-config.json` 25 | 26 | ``` bash 27 | npm install 28 | npm run build 29 | 30 | go build 31 | go install 32 | 33 | PORT=8000 $GOPATH/bin/goex-search 34 | # running at http://localhost:8000 35 | ``` 36 | 37 | ## Built With 38 | - Golang 39 | - Echo 40 | - Vue.js 41 | - Bulma 42 | -------------------------------------------------------------------------------- /api-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "yelp": { 3 | "client_id": "", 4 | "client_secret": "" 5 | }, 6 | "google-maps": { 7 | "key": "" 8 | }, 9 | "watson": { 10 | "language_understanding": { 11 | "username": "", 12 | "password": "" 13 | } 14 | }, 15 | "text-analysis": { 16 | "app_id": "", 17 | "key": "" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /google-places.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/net/context" 6 | "googlemaps.github.io/maps" 7 | ) 8 | 9 | type GooglePlaceReview struct { 10 | AuthorName string `json:"author_name"` 11 | AuthorURL string `json:"author_url"` 12 | Rating int `json:"rating"` 13 | Text string `json:"text"` 14 | Time int `json:"time"` 15 | } 16 | 17 | type GooglePlaceReviews struct { 18 | Reviews []*GooglePlaceReview `json:"reviews"` 19 | } 20 | 21 | func SearchGoogleReviews(args *SummaryForm, conf *ApiKeys) (string, *GooglePlaceReviews, error) { // get 5 reviews for a business 22 | c, err := maps.NewClient(maps.WithAPIKey(conf.GoogleMaps.Key)) 23 | if err != nil { 24 | fmt.Println(err) 25 | } 26 | 27 | fmt.Println(args) 28 | 29 | r := &maps.NearbySearchRequest{ 30 | Location: &maps.LatLng{ 31 | Lat: args.Latitude, 32 | Lng: args.Longitude, 33 | }, 34 | Keyword: args.Name, 35 | RankBy: "distance", 36 | } 37 | 38 | resp, err := c.NearbySearch(context.Background(), r) 39 | if err != nil { 40 | fmt.Println(err) 41 | } 42 | 43 | if len(resp.Results) > 0 { 44 | matchedPlace := resp.Results[0] 45 | fmt.Println("looking for: " + args.Name + " found: " + matchedPlace.Name) 46 | 47 | r2 := &maps.PlaceDetailsRequest{ 48 | PlaceID: matchedPlace.PlaceID, 49 | } 50 | details, err := c.PlaceDetails(context.Background(), r2) 51 | if err != nil { 52 | fmt.Println(err) 53 | } 54 | 55 | gReviews := new(GooglePlaceReviews) 56 | 57 | for x := range details.Reviews { 58 | gReview := new(GooglePlaceReview) 59 | gReview.AuthorName = details.Reviews[x].AuthorName 60 | gReview.AuthorURL = details.Reviews[x].AuthorURL 61 | gReview.Rating = details.Reviews[x].Rating 62 | gReview.Text = details.Reviews[x].Text 63 | gReview.Time = details.Reviews[x].Time 64 | gReviews.Reviews = append(gReviews.Reviews, gReview) 65 | } 66 | 67 | reviews := "" 68 | for x := range details.Reviews { 69 | reviews += details.Reviews[x].Text + " . " 70 | } 71 | return reviews, gReviews, nil 72 | 73 | } 74 | return "", nil, err 75 | } 76 | 77 | func GetReviews(c *maps.Client, b *maps.PlaceDetailsRequest) ([]maps.PlaceReview, error) { // get reviews for a business 78 | resp, err := c.PlaceDetails(context.Background(), b) 79 | if err != nil { 80 | fmt.Println(err) 81 | } 82 | 83 | return resp.Reviews, err 84 | } 85 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Goex 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | ) 7 | 8 | var yelpToken *ApiToken // yelp authenticator 9 | var apiKeys *ApiKeys 10 | 11 | const ( 12 | Conf_Path = "api-config.json" // path to api key file 13 | ) 14 | 15 | type ApiKeys struct { // hold api credentials 16 | Yelp struct { 17 | Id string `json:"client_id"` 18 | Secret string `json:"client_secret"` 19 | } `json:"yelp"` 20 | GoogleMaps struct { 21 | Key string `json:"key"` 22 | } `json:"google-maps"` 23 | Watson struct { 24 | ToneAnalysis struct { 25 | Username string `json: password` 26 | Password string `json:"password"` 27 | } `json:"tone_analysis"` 28 | LanguageUnderstanding struct { 29 | Username string `json: password` 30 | Password string `json:"password"` 31 | } `json:"language_understanding"` 32 | } `json:"watson"` 33 | TextApi struct { 34 | AppId string `json:"app_id"` 35 | Key string `json:"key"` 36 | } `json:"text-analysis"` 37 | } 38 | 39 | func main() { 40 | 41 | conf, err := ReadConfigFile(Conf_Path) // parse api key file 42 | if err != nil { 43 | panic(err) 44 | } 45 | apiKeys = conf 46 | 47 | yelpToken, err = GetApiToken(conf) // get yelp authenticator 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | Serve() // start server 53 | 54 | } 55 | 56 | func ReadConfigFile(path string) (*ApiKeys, error) { // parse api keys from json 57 | 58 | configFile, err := ioutil.ReadFile(path) 59 | if err != nil { 60 | return nil, err 61 | } 62 | var a = new(ApiKeys) 63 | err_ := json.Unmarshal([]byte(configFile), &a) 64 | return a, err_ 65 | } 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goex", 3 | "description": "A Vue.js project", 4 | "version": "1.0.0", 5 | "author": "Johann Miller", 6 | "private": true, 7 | "scripts": { 8 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", 9 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 10 | }, 11 | "dependencies": { 12 | "axios": "^0.15.3", 13 | "bulma": "^0.4.0", 14 | "lodash": "^4.17.4", 15 | "vue": "^2.2.1", 16 | "vue-router": "^2.3.1", 17 | "vue-scrollto": "^2.6.4" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "^6.0.0", 21 | "babel-loader": "^6.0.0", 22 | "babel-preset-latest": "^6.0.0", 23 | "cross-env": "^3.0.0", 24 | "css-loader": "^0.25.0", 25 | "file-loader": "^0.9.0", 26 | "node-sass": "^4.5.0", 27 | "sass-loader": "^5.0.1", 28 | "vue-loader": "^11.1.4", 29 | "vue-template-compiler": "^2.2.1", 30 | "webpack": "^2.2.0", 31 | "webpack-dev-server": "^2.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/labstack/echo" 7 | // "github.com/labstack/echo/middleware" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | ) 12 | 13 | type SearchForm struct { // general search request from spa 14 | Term string `json:"term"` 15 | Location string `json:"location"` 16 | Limit string `json:"limit"` 17 | } 18 | 19 | type SummaryForm struct { // place summary request from spa 20 | Name string `json:"name"` 21 | Latitude float64 `json:"latitude"` 22 | Longitude float64 `json:"longitude"` 23 | } 24 | 25 | func Serve() { 26 | 27 | e := echo.New() 28 | e.File("/", "index.html") 29 | e.Static("/dist", "dist") 30 | e.Static("/static", "static") 31 | e.POST("/places", postPlaces) 32 | e.POST("/summary", postSummary) 33 | 34 | // e.Use(middleware.CORS()) // TODO: remove for production 35 | 36 | e.Logger.Fatal(e.Start(":" + os.Getenv("PORT"))) 37 | } 38 | 39 | func postPlaces(c echo.Context) (err error) { // respond to place search post 40 | 41 | var u = new(SearchForm) 42 | 43 | if err = c.Bind(u); err != nil { 44 | return err 45 | } 46 | 47 | form := url.Values{} 48 | form.Add("location", u.Location) 49 | form.Add("term", u.Term) 50 | form.Add("limit", u.Limit) 51 | 52 | res, err := YelpSearch(form.Encode(), yelpToken) 53 | if err != nil { 54 | fmt.Println(err) 55 | } 56 | 57 | out, err := json.Marshal(res) 58 | if err != nil { 59 | fmt.Println(err) 60 | } 61 | 62 | return c.String(http.StatusOK, string(out)) 63 | } 64 | 65 | func postSummary(c echo.Context) (err error) { // respond to place summary post 66 | var u = new(SummaryForm) 67 | if err = c.Bind(u); err != nil { 68 | return err 69 | } 70 | 71 | text, gReviews, err := SearchGoogleReviews(u, apiKeys) 72 | if err != nil { 73 | fmt.Println(err) 74 | } 75 | 76 | res, err := Summarize(text, apiKeys) 77 | if err != nil { 78 | fmt.Println(err) 79 | } 80 | 81 | res.Reviews = gReviews 82 | 83 | out, err := json.Marshal(res) 84 | if err != nil { 85 | fmt.Println(err) 86 | } 87 | 88 | return c.String(http.StatusOK, string(out)) 89 | } 90 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 50 | 51 | 54 | 55 | 98 | 99 | 124 | -------------------------------------------------------------------------------- /src/components/BusinessBox.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 128 | 129 | 187 | -------------------------------------------------------------------------------- /src/components/LoadingIcon.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 88 | -------------------------------------------------------------------------------- /src/components/NavBar.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 45 | 46 | 92 | -------------------------------------------------------------------------------- /src/components/ReviewBox.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 91 | 92 | 105 | -------------------------------------------------------------------------------- /src/components/YelpStars.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 34 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import SearchForm from './pages/SearchForm.vue' 4 | import VueScrollTo from 'vue-scrollto' 5 | import Router from 'vue-router' 6 | 7 | Vue.use(VueScrollTo) 8 | Vue.use(Router) 9 | 10 | const routes = [ 11 | { name: 'search', path: '/', component: SearchForm } 12 | ] 13 | 14 | const router = new Router({ 15 | routes 16 | }) 17 | 18 | const app = new Vue({ 19 | router, 20 | render: h => h(App) 21 | }).$mount('#app') 22 | -------------------------------------------------------------------------------- /src/pages/SearchForm.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 196 | 197 | 241 | -------------------------------------------------------------------------------- /static/img/yelp_stars/small_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_0.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_0@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_0@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_1.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_1@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_1@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_1_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_1_half.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_1_half@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_1_half@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_1_half@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_1_half@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_2.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_2@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_2@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_2_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_2_half.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_2_half@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_2_half@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_2_half@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_2_half@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_3.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_3@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_3@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_3@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_3_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_3_half.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_3_half@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_3_half@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_3_half@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_3_half@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_4.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_4@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_4@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_4@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_4_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_4_half.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_4_half@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_4_half@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_4_half@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_4_half@3x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_5.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_5@2x.png -------------------------------------------------------------------------------- /static/img/yelp_stars/small_5@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johannkm/goex-search/01c37815e76a68f90341d44f63800cad098f110a/static/img/yelp_stars/small_5@3x.png -------------------------------------------------------------------------------- /text-process.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | textapi "github.com/AYLIEN/aylien_textapi_go" 8 | "io/ioutil" 9 | "net/http" 10 | ) 11 | 12 | var TextApiClient *textapi.Client 13 | 14 | type WatsonToneResponse struct { 15 | DocumentTone struct { 16 | ToneCategories []struct { 17 | CategoryName string `json:"category_name"` 18 | CategoryId string `json:"category_id"` 19 | Tones []struct { 20 | Score float64 `json:"score"` 21 | ToneId string `json:"tone_id"` 22 | ToneName string `json:"tone_name"` 23 | } `json:"tones"` 24 | } `json:"tone_categories"` 25 | } `json:"document_tone"` 26 | } 27 | 28 | type SummaryResponse struct { 29 | Keyword string `json:"keyword"` 30 | Text string `json:"text"` 31 | KeywordSentiment float32 `json:"keyword_sentiment"` 32 | Reviews *GooglePlaceReviews `json:"google_place_review"` 33 | } 34 | 35 | type WatsonUnderstandRequest struct { 36 | Text string `json:"text"` 37 | Features struct { 38 | Keywords struct { 39 | Emotion bool `json:"emotion"` 40 | Sentiment bool `json:"sentiment"` 41 | Limit int32 `json:"limit"` 42 | } `json:"keywords"` 43 | } `json:"features"` 44 | } 45 | 46 | type WatsonUnderstandResponse struct { 47 | Keywords []struct { 48 | Text string `json:"text"` 49 | Sentiment struct { 50 | Score float32 `json:"score"` 51 | } `json:"sentiment"` 52 | } `json:"keywords"` 53 | } 54 | 55 | func Summarize(reviews string, creds *ApiKeys) (resp *SummaryResponse, err error) { // return summary for business reviews 56 | 57 | if TextApiClient == nil { 58 | auth := textapi.Auth{creds.TextApi.AppId, creds.TextApi.Key} 59 | TextApiClient, err = textapi.NewClient(auth, true) 60 | if err != nil { 61 | fmt.Println(err) 62 | } 63 | } 64 | 65 | fmt.Println("incoming review text: " + reviews) 66 | summarizeParams := &textapi.SummarizeParams{ 67 | Title: "Review", 68 | Text: reviews, 69 | NumberOfSentences: 1, 70 | Mode: "short", 71 | } 72 | summary, err := TextApiClient.Summarize(summarizeParams) 73 | if err != nil { 74 | fmt.Println(err) 75 | } 76 | fmt.Printf("%v\n", TextApiClient.RateLimits) 77 | 78 | resp = new(SummaryResponse) 79 | summaryText := "" 80 | for x := range summary.Sentences { 81 | summaryText = summaryText + summary.Sentences[x] 82 | } 83 | 84 | understanding, err := FindKeyword(creds, reviews) 85 | if err != nil { 86 | fmt.Println(err) 87 | } 88 | 89 | title := understanding.Keywords[0].Text 90 | keywordSentiment := understanding.Keywords[0].Sentiment.Score 91 | fmt.Println("title: " + title) 92 | 93 | resp.Text = summaryText 94 | resp.Keyword = title 95 | resp.KeywordSentiment = keywordSentiment 96 | 97 | fmt.Println("outgoing summary" + summaryText) 98 | return resp, nil 99 | } 100 | 101 | func AnalyzeTone(creds *ApiKeys, text string) (*WatsonToneResponse, error) { 102 | client := &http.Client{} 103 | req, err := http.NewRequest("GET", "https://gateway.watsonplatform.net/tone-analyzer/api/v3/tone", nil) 104 | if err != nil { 105 | return nil, err 106 | } 107 | req.SetBasicAuth(creds.Watson.ToneAnalysis.Username, creds.Watson.ToneAnalysis.Password) 108 | q := req.URL.Query() 109 | q.Add("version", "2016-05-19") 110 | q.Add("text", text) 111 | q.Add("sentences", "false") 112 | req.URL.RawQuery = q.Encode() 113 | 114 | resp, err := client.Do(req) 115 | if err != nil { 116 | return nil, err 117 | } 118 | 119 | bodyBytes, err := ioutil.ReadAll(resp.Body) 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | var r = new(WatsonToneResponse) 125 | err = json.Unmarshal([]byte(bodyBytes), &r) 126 | if err != nil { 127 | fmt.Println(err) 128 | } 129 | return r, err 130 | } 131 | 132 | func FindKeyword(creds *ApiKeys, text string) (*WatsonUnderstandResponse, error) { 133 | 134 | reqBody := new(WatsonUnderstandRequest) 135 | reqBody.Text = text 136 | reqBody.Features.Keywords.Emotion = false 137 | reqBody.Features.Keywords.Sentiment = true 138 | reqBody.Features.Keywords.Limit = 1 139 | 140 | reqBodyJson, err := json.Marshal(reqBody) 141 | if err != nil { 142 | fmt.Println(err) 143 | } 144 | 145 | client := &http.Client{} 146 | req, err := http.NewRequest("POST", "https://gateway.watsonplatform.net/natural-language-understanding/api/v1/analyze", bytes.NewBuffer(reqBodyJson)) 147 | if err != nil { 148 | return nil, err 149 | } 150 | req.SetBasicAuth(creds.Watson.LanguageUnderstanding.Username, creds.Watson.LanguageUnderstanding.Password) 151 | q := req.URL.Query() 152 | q.Add("version", "2017-02-27") 153 | req.URL.RawQuery = q.Encode() 154 | req.Header.Set("Content-Type", "application/json") 155 | 156 | resp, err := client.Do(req) 157 | if err != nil { 158 | return nil, err 159 | } 160 | 161 | bodyBytes, err := ioutil.ReadAll(resp.Body) 162 | if err != nil { 163 | return nil, err 164 | } 165 | 166 | var r = new(WatsonUnderstandResponse) 167 | err = json.Unmarshal([]byte(bodyBytes), &r) 168 | if err != nil { 169 | fmt.Println(err) 170 | } 171 | return r, err 172 | } 173 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: './src/main.js', 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | publicPath: '/dist/', 9 | filename: 'build.js' 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.vue$/, 15 | loader: 'vue-loader', 16 | options: { 17 | loaders: { 18 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 19 | // the "scss" and "sass" values for the lang attribute to the right configs here. 20 | // other preprocessors should work out of the box, no loader config like this necessary. 21 | 'scss': 'vue-style-loader!css-loader!sass-loader', 22 | 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax' 23 | } 24 | // other vue-loader options go here 25 | } 26 | }, 27 | { 28 | test: /\.js$/, 29 | loader: 'babel-loader', 30 | exclude: /node_modules/ 31 | }, 32 | { 33 | test: /\.(png|jpg|gif|svg)$/, 34 | loader: 'file-loader', 35 | options: { 36 | name: '[name].[ext]?[hash]' 37 | } 38 | } 39 | ] 40 | }, 41 | resolve: { 42 | alias: { 43 | 'vue$': 'vue/dist/vue.esm.js' 44 | } 45 | }, 46 | devServer: { 47 | historyApiFallback: true, 48 | noInfo: true 49 | }, 50 | performance: { 51 | hints: false 52 | }, 53 | devtool: '#eval-source-map' 54 | } 55 | 56 | if (process.env.NODE_ENV === 'production') { 57 | module.exports.devtool = '#source-map' 58 | // http://vue-loader.vuejs.org/en/workflow/production.html 59 | module.exports.plugins = (module.exports.plugins || []).concat([ 60 | new webpack.DefinePlugin({ 61 | 'process.env': { 62 | NODE_ENV: '"production"' 63 | } 64 | }), 65 | new webpack.optimize.UglifyJsPlugin({ 66 | sourceMap: true, 67 | compress: { 68 | warnings: false 69 | } 70 | }), 71 | new webpack.LoaderOptionsPlugin({ 72 | minimize: true 73 | }) 74 | ]) 75 | } 76 | -------------------------------------------------------------------------------- /yelp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | const ( // yelp parameters 12 | Token_Url = "https://api.yelp.com/oauth2/token" 13 | Post_Format = "application/x-www-form-urlencoded" 14 | ) 15 | 16 | type ApiToken struct { 17 | AccessToken string `json:"access_token"` 18 | TokenType string `json:"token_type"` 19 | ExpiresIn int64 `json:"expires_in"` 20 | } 21 | 22 | type Business struct { 23 | Rating float32 `json:"rating"` 24 | Price string `json:"price"` 25 | Id string `json:"id"` 26 | Name string `json:"name"` 27 | Url string `json:"url"` 28 | Phone string `json:"phone"` 29 | RatingCount int32 `json:"review_count"` 30 | ImgUrl string `json:"image_url"` 31 | Hours []struct { 32 | HoursType string `json:"hours_type"` 33 | Open []struct { 34 | Day int32 `json:"day"` 35 | Start string `json:"start"` 36 | End string `json:"end"` 37 | Overnight bool `json:"is_overnight"` 38 | } `json:"open"` 39 | } `json:"hours"` 40 | IsClosed bool `json:"is_closed"` 41 | Categories []struct { 42 | Alias string `json:"alias"` 43 | Title string `json:"title"` 44 | } `json:"categories"` 45 | Coordinates struct { 46 | Latitude float64 `json:"latitude"` 47 | Longitude float64 `json:"longitude"` 48 | } `json:"coordinates"` 49 | Analysis struct { 50 | Emotion string `json:"emotion"` 51 | } `json:"analysis"` 52 | } 53 | 54 | type YelpSearchResp struct { 55 | Total int64 `json:"total"` 56 | Businesses []Business `json:"businesses"` 57 | } 58 | 59 | type YelpReviewSearchResp struct { 60 | Total int32 `json:"total"` 61 | Reviews []struct { 62 | Text string `json:"text"` 63 | Url string `json:"url"` 64 | Rating int32 `json:"rating"` 65 | TimeCreated string `json:"time_created"` 66 | User struct { 67 | Name string `json:"name"` 68 | ImageUrl string `json:"image_url"` 69 | } `json:"user"` 70 | } `json:"reviews"` 71 | } 72 | 73 | func GetApiToken(conf *ApiKeys) (*ApiToken, error) { // get credentials 74 | 75 | form := url.Values{} 76 | form.Add("grant_type", "client_credentials") 77 | form.Add("client_id", conf.Yelp.Id) 78 | form.Add("client_secret", conf.Yelp.Secret) 79 | 80 | resp, err := PostId(Token_Url, Post_Format, form.Encode()) 81 | if err != nil { 82 | panic(err) 83 | } 84 | cred, err := ParseApiToken(resp) 85 | return cred, err 86 | } 87 | 88 | func PostId(url string, format string, args string) ([]byte, error) { // send api secret 89 | 90 | argsBytes := bytes.NewBuffer([]byte(args)) 91 | resp, err := http.Post(url, format, argsBytes) 92 | 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | defer resp.Body.Close() 98 | bodyBytes, err := ioutil.ReadAll(resp.Body) 99 | 100 | return []byte(bodyBytes), err 101 | } 102 | 103 | func ParseApiToken(body []byte) (*ApiToken, error) { // retreive token 104 | 105 | var c = new(ApiToken) 106 | err := json.Unmarshal(body, &c) 107 | 108 | return c, err 109 | } 110 | 111 | func MakeGet(url string, cred *ApiToken) (*http.Response, error) { // http get request 112 | 113 | auth := cred.TokenType + " " + cred.AccessToken 114 | 115 | client := &http.Client{} 116 | req, err := http.NewRequest("GET", url, nil) 117 | if err != nil { 118 | return nil, err 119 | } 120 | req.Header.Set("Authorization", auth) 121 | res, err := client.Do(req) 122 | 123 | return res, err 124 | 125 | } 126 | 127 | func YelpSearch(args string, cred *ApiToken) (*YelpSearchResp, error) { // yelp business search 128 | 129 | addr := "https://api.yelp.com/v3/businesses/search" + "?" + args 130 | resp, err := MakeGet(addr, cred) 131 | if err != nil { 132 | return nil, err 133 | } 134 | bodyBytes, err := ioutil.ReadAll(resp.Body) 135 | if err != nil { 136 | return nil, err 137 | } 138 | 139 | var r = new(YelpSearchResp) 140 | err_ := json.Unmarshal([]byte(bodyBytes), &r) 141 | 142 | return r, err_ 143 | 144 | } 145 | 146 | func YelpReviewSearch(id string, cred *ApiToken) (*YelpReviewSearchResp, error) { // yelp review search 147 | addr := "https://api.yelp.com/v3/businesses/" + id + "/reviews" 148 | 149 | resp, err := MakeGet(addr, cred) 150 | if err != nil { 151 | return nil, err 152 | } 153 | bodyBytes, err := ioutil.ReadAll(resp.Body) 154 | if err != nil { 155 | return nil, err 156 | } 157 | 158 | var r = new(YelpReviewSearchResp) 159 | err_ := json.Unmarshal([]byte(bodyBytes), &r) 160 | 161 | return r, err_ 162 | } 163 | --------------------------------------------------------------------------------