36
"github.com/NOX73/go-oauth"
37
"github.com/gosexy/gettext"
38
"github.com/kellydunn/golang-geo"
39
"launchpad.net/go-unityscopes/v2"
35
"github.com/gosexy/gettext"
36
"github.com/kellydunn/golang-geo"
37
"launchpad.net/go-unityscopes/v2"
42
// "github.com/NOX73/go-oauth"
43
//"launchpad.net/go-unityscopes/v2"
45
const searchCategoryTemplate = `{
48
"category-layout": "grid",
49
"card-size": "medium",
55
"subtitle": "address",
56
"attributes": "attributes"
60
const searchCategoryTemplate2 = `{
63
"category-layout": "grid",
65
"card-layout": "horizontal",
71
"subtitle": "address",
72
"attributes": "attributes"
76
40
const errorTemplate = `{
79
"category-layout": "grid",
81
"card-layout": "horizontal",
86
"subtitle": "subtitle"
43
"category-layout": "grid",
45
"card-layout": "horizontal",
50
"subtitle": "subtitle"
54
type PriceFilter struct {
55
using bool //whether this is/is not an active filter
56
unknown bool //include businesses that do not state a price range
57
one bool //inexpensive
60
four bool ///ultra high end
90
63
type YelpScope struct {
98
Settings map[string]int
99
Base *scopes.ScopeBase
70
Settings map[string]int
71
Base *scopes.ScopeBase
102
74
type actionInfo struct {
103
Id string `json:"id"`
104
Label string `json:"label"`
105
Icon string `json:"icon,omitempty"`
106
Uri string `json:"uri,omitempty"`
76
Label string `json:"label"`
77
Icon string `json:"icon,omitempty"`
78
Uri string `json:"uri,omitempty"`
109
81
type ratingInfo struct {
110
Ratingiconempty string
111
Ratingiconhalf string
112
Ratingiconfull string
82
Ratingiconempty string
116
88
type reviewInfo struct {
117
Rating float32 `json:"rating"`
118
Author string `json:"author"`
119
Review string `json:"review,omitempty"`
89
Rating float32 `json:"rating"`
90
Author string `json:"author"`
91
Review string `json:"review,omitempty"`
122
94
type attributeInfo struct {
123
Value string `json:"value,omitempty"`
124
Icon string `json:"icon,omitempty"`
95
Value string `json:"value,omitempty"`
96
Icon string `json:"icon,omitempty"`
126
98
type location struct {
127
Display_address []string `json:"display_address"`
128
Coord coord `json:"coordinate"`
99
City string `json:"city"`
100
Country string `json:"country"`
101
Address1 string `json:"address1"`
102
Address2 string `json:"address2"`
103
Address3 string `json:"address3"`
104
State string `json:"state"`
105
Zip string `json:"zip_code"`
131
108
type coord struct {
132
Latitude float64 `json:"latitude"`
133
Longitude float64 `json:"longitude"`
109
Latitude float64 `json:"latitude"`
110
Longitude float64 `json:"longitude"`
113
type category struct {
114
Alias string `json:"alias"`
115
Title string `json:"title"`
136
118
type place struct {
137
Name string `json:"name"`
138
Image_url string `json:"image_url"`
139
Location location `json:"location"`
140
Phone string `json:"phone"`
141
Rating float32 `json:"rating"`
142
Url string `json:"mobile_url"`
143
ID string `json:"id"`
144
RatingIcon string `json:"rating_img_url_large"`
145
Cats [][]string `json:"categories"`
146
ReviewCount int `json:"review_count"`
147
RestaurantsPriceRange int `json:"RestaurantsPriceRange2"`
148
IsClosed bool `json:"is_closed"`
149
Hours []string `json:"hours"`
119
Name string `json:"name"`
120
Location coord `json:"coordinates"`
121
Rating float32 `json:"rating"`
122
Id string `json:"id"`
123
Cats []category `json:"categories"`
124
ReviewCount int `json:"review_count"`
125
Url string `json:"url"`
156
128
type deptlist struct {
157
Depts []dept `json:"data"`
129
Depts []dept `json:"data"`
159
131
type dept struct {
160
Name string `json:"_name"`
161
Label string `json:"label"`
162
Subs []dept `json:"subdepartments"`
132
Name string `json:"_name"`
133
Label string `json:"label"`
134
Subs []dept `json:"subdepartments"`
165
137
type yelpError struct {
166
Text string `json:"text"`
138
Text string `json:"text"`
169
141
type yelpAnswer struct {
170
Businesses []place `json:"businesses"`
171
Error yelpError `json:"error"`
174
func (yl *YelpScope) buildUrl(params map[string]string) string {
175
query := make(url.Values)
176
for key, value := range params {
177
query.Set(key, value)
179
return yl.BaseURI + query.Encode()
182
func (yl *YelpScope) get(params map[string]string, result interface{}) error {
183
credentials := oauth.NewCredentials(yl.ConsumerKey, yl.Token, yl.ConsumerSecret, yl.TokenSecret)
184
var client http.Client
185
var method string = "GET"
186
var url string = yl.buildUrl(params)
187
req, _ := oauth.NewRequest(method, url, params, credentials)
189
resp, _ := client.Do(req.HttpRequest())
190
defer resp.Body.Close()
191
decoder := json.NewDecoder(resp.Body)
192
return decoder.Decode(result)
142
Businesses []place `json:"businesses"`
143
Count int64 `json:"total"`
146
type tokenResult struct {
147
AccessToken string `json:"access_token"`
148
TokenType string `json:"token_type"`
149
ExpiresIn int64 `json:"expries_in"`
152
type business struct {
153
ImageUrl string `json:"image_url"`
154
Url string `json:"url"`
155
Phone string `json:"phone"`
156
Photos []string `json:"photos"`
157
Location location `json:"location"`
161
type reviews struct {
162
Reviews []review `json:"reviews"`
166
Url string `json:"url"`
167
Text string `json:"text"`
168
Rating float32 `json:"rating"`
171
func (yl *YelpScope) buildUrl(params map[string]string, endpoint string) string {
172
query := make(url.Values)
173
for key, value := range params {
174
query.Set(key, value)
176
return yl.BaseURI + endpoint + query.Encode()
179
func (yl *YelpScope) get(params map[string]string, endpoint string, result interface{}) error {
180
var client http.Client
181
var method string = "GET"
182
var url string = yl.buildUrl(params, endpoint)
183
log.Println("YELP scope uri:", url)
184
req, _ := http.NewRequest(method, url, nil)
185
log.Println("YELP enweitoken1:", strings.TrimSpace(yl.AccessToken))
186
if len(strings.TrimSpace(yl.AccessToken)) == 0 {
189
log.Println("YELP enweitoken2:", yl.AccessToken)
190
req.Header.Add("Authorization", "Bearer "+yl.AccessToken)
191
resp, _ := client.Do(req)
192
defer resp.Body.Close()
193
decoder := json.NewDecoder(resp.Body)
194
return decoder.Decode(result)
197
func (yl *YelpScope) getAccessToken() error {
198
var client http.Client
199
param := map[string]string{"grant_type": "client_credentials", "client_id": yl.ClientId, "client_secret": yl.ClientSecret}
200
query := make(url.Values)
201
for key, value := range param {
202
query.Set(key, value)
204
req_body := bytes.NewBufferString(query.Encode())
205
req, _ := http.NewRequest("POST", "https://api.yelp.com/oauth2/token", req_body)
206
req.Header.Add("content-type", "application/x-www-form-urlencoded;charset=UTF-8")
207
resp, err := client.Do(req)
208
defer resp.Body.Close()
210
log.Println("YELP scope getAccessToken error", err.Error())
213
var result tokenResult
214
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
218
json.Unmarshal(bodyBytes, &result)
219
yl.AccessToken = result.AccessToken
196
223
func (yl *YelpScope) getDepts(result interface{}) error {
197
deptfile, _ := os.Open(yl.Dir + "/depts.json")
198
decoder := json.NewDecoder(deptfile)
199
return decoder.Decode(result)
224
deptfile, _ := os.Open(yl.Dir + "/depts.json")
225
decoder := json.NewDecoder(deptfile)
226
return decoder.Decode(result)
202
229
func (yl *YelpScope) getLayout() string {
203
layoutfile, _ := ioutil.ReadFile(yl.Dir + "/layout.json")
204
return string(layoutfile)
230
layoutfile, _ := ioutil.ReadFile(yl.Dir + "/layout.json")
231
return string(layoutfile)
207
234
func (yl *YelpScope) SetScopeBase(base *scopes.ScopeBase) {
210
gettext.SetLocale(gettext.LC_ALL, "")
211
gettext.SetLocale(gettext.LC_NUMERIC, "C")
212
gettext.BindTextdomain("yelp", yl.Base.ScopeDirectory()+"/locale/")
213
gettext.Textdomain("yelp")
237
gettext.SetLocale(gettext.LC_ALL, "")
238
gettext.SetLocale(gettext.LC_NUMERIC, "C")
239
gettext.BindTextdomain("yelp", yl.Base.ScopeDirectory()+"/locale/")
240
gettext.Textdomain("yelp")
217
244
func KmtoMi(km float64) float64 {
221
type RatingFilter struct {
222
using bool //whether this is/is not an active filter
234
type PriceFilter struct {
235
using bool //whether this is/is not an active filter
236
unknown bool //include businesses that do not state a price range
237
one bool //inexpensive
240
four bool ///ultra high end
243
248
func (yl *YelpScope) Search(q *scopes.CannedQuery, metadata *scopes.SearchMetadata, reply *scopes.SearchReply, cancelled <-chan bool) error {
244
query := q.QueryString()
245
loc := metadata.Location()
247
locale := metadata.Locale()
248
langstring := strings.Split(locale, "_")
250
if langstring[1] == "US" {
253
minDistanceLabel := "0 km"
254
var maxDistance float64
255
maxDistance = 30 //km
256
var maxDistanceLabel string
257
maxDistanceLabel = "30 km"
258
if distunit == "mi" {
259
minDistanceLabel = "0 mi"
261
maxDistanceLabel = "20 mi"
264
var numResultsDefault float64
265
numResultsDefault = 40
266
numResultsFilter := scopes.NewValueSliderFilter("sliderResults", 10, 300, numResultsDefault,
267
scopes.ValueSliderLabels{
272
//Translators: This is the maximum number of Yelp results to display
273
numResultsFilter.Title = gettext.Gettext("Max results")
275
// distances in code always use km.
276
// slider gui uses mi (miles) in US
277
// When in US, the slider value is coverted to km on retrieval
278
var distanceDefault float64
280
distanceFilter := scopes.NewValueSliderFilter("distance", 0, maxDistance, distanceDefault,
281
scopes.ValueSliderLabels{
282
MinLabel: minDistanceLabel,
283
MaxLabel: maxDistanceLabel,
286
distanceFilter.Title = gettext.Gettext("Distance")
288
curSymb := C.GoString(C.localeconv().currency_symbol)
289
priceOneLabel := curSymb
290
priceTwoLabel := curSymb + curSymb
291
priceThreeLabel := curSymb + curSymb + curSymb
292
priceFourLabel := curSymb + curSymb + curSymb + curSymb
294
filterState := q.FilterState()
296
var ratingDefault float64
298
ratingFilter := scopes.NewValueSliderFilter("rating", 0, 5, ratingDefault,
299
scopes.ValueSliderLabels{
304
ratingFilter.Title = gettext.Gettext("Lowest rating")
306
priceFilter := scopes.NewOptionSelectorFilter("priceFilter", gettext.Gettext("Price range"), true)
307
priceFilter.AddOption("onePrice", priceOneLabel, false)
308
priceFilter.AddOption("twoPrice", priceTwoLabel, false)
309
priceFilter.AddOption("threePrice", priceThreeLabel, false)
310
priceFilter.AddOption("fourPrice", priceFourLabel, false)
312
//chnage to Switch filter when it moves from experimental namespace
313
openFilter := scopes.NewOptionSelectorFilter("openFilter", gettext.Gettext("Open"), false)
314
openFilter.AddOption("isOpen", gettext.Gettext("Is open"), false)
316
filters := []scopes.Filter{priceFilter, ratingFilter, openFilter, distanceFilter, numResultsFilter}
318
errP := reply.PushFilters(filters, q.FilterState())
320
return errors.New("yelp scope: cannot push filters. Error: " + errP.Error())
323
//chnage to Switch filter when it moves from experimental namespace
324
//currentOpenFilter := openFilter.IsOn(filterState)
325
currentOpenFilter := false
326
if openFilter.HasActiveOption(filterState) {
327
for _, option := range openFilter.ActiveOptions(filterState) {
328
if option == "isOpen" {
329
currentOpenFilter = true
333
var currentPriceFilter PriceFilter
334
currentPriceFilter.using = false
335
if priceFilter.HasActiveOption(filterState) {
336
for _, option := range priceFilter.ActiveOptions(filterState) {
339
currentPriceFilter.unknown = true
340
currentPriceFilter.using = true
342
currentPriceFilter.one = true
343
currentPriceFilter.using = true
345
currentPriceFilter.two = true
346
currentPriceFilter.using = true
348
currentPriceFilter.three = true
349
currentPriceFilter.using = true
351
currentPriceFilter.four = true
352
currentPriceFilter.using = true
357
//setup locale, scope directory, layout, distance units, etc
358
yl.Dir = yl.Base.ScopeDirectory()
359
yl.Layout = yl.getLayout()
361
//get all the departments setup
362
rootDept, err := scopes.NewDepartment("", q, gettext.Gettext("Everything"))
366
var deptlist1 deptlist
367
err = yl.getDepts(&deptlist1)
368
for _, v := range deptlist1.Depts {
369
newdept, _ := scopes.NewDepartment(v.Label, q, gettext.Gettext(v.Name))
370
rootDept.AddSubdepartment(newdept)
371
if len(v.Subs) != 0 {
372
for _, s := range v.Subs {
373
newdept2, _ := scopes.NewDepartment(s.Label, q, gettext.Gettext(s.Name))
374
newdept.AddSubdepartment(newdept2)
378
reply.RegisterDepartments(rootDept)
380
numResults_f := numResultsFilter.Value(filterState)
382
//log.Printf("==== Yelp. num reuslts value: %v\n", numResults_f)
384
//log.Printf("=== YELP. num results: %v\n", numResults_f)
385
// determine number of times to call yelp api to get the number of results we want
386
numResults := int(numResults_f)
389
iters_f = numResults_f / 20
390
iters_s := strconv.FormatFloat(iters_f, 'f', 1, 64)
391
parts := strings.Split(iters_s, ".")
392
iters, _ = strconv.Atoi(parts[0])
393
itersDec, _ := strconv.Atoi(parts[1])
398
var answers yelpAnswer
400
//if loc is nil, there's nothing we can search for
402
ll := fmt.Sprintf("%v,%v", loc.Latitude, loc.Longitude)
403
remainingResults := numResults
405
for i := 0; i < iters; i++ {
406
var response yelpAnswer
408
offset_s := strconv.Itoa(offset)
410
if remainingResults < 20 {
411
limit = strconv.Itoa(remainingResults)
413
queryParams := map[string]string{"ll": ll, "cc": langstring[1], "lang": langstring[0], "limit": limit, "offset": offset_s}
414
if len(q.DepartmentID()) > 0 {
415
queryParams["category_filter"] = q.DepartmentID()
418
queryParams["term"] = query
420
// call to the web api
421
if err := yl.get(queryParams, &response); err != nil {
424
if response.Error.Text != "" {
425
log.Println(response.Error.Text)
427
remainingResults = remainingResults - 20
428
answers.Businesses = append(answers.Businesses, response.Businesses...)
431
//sort answers into answersByDistanceName
433
var distanceKeys []float64
434
var places map[float64]map[string]place // distance key, names string
435
places = make(map[float64]map[string]place)
436
point1 := geo.NewPoint(loc.Latitude, loc.Longitude)
437
for _, p := range answers.Businesses {
440
if p.Location.Coord.Latitude != 0 && p.Location.Coord.Longitude != 0 {
441
point2 := geo.NewPoint(p.Location.Coord.Latitude, p.Location.Coord.Longitude)
442
dist = point1.GreatCircleDistance(point2)
444
if distunit == "mi" {
447
//dist_s := strconv.FormatFloat(dist, 'f', 2, 64)
448
//key := dist_s + "_" + place.Name
450
if _, ok := places[dist]; !ok {
451
places[dist] = make(map[string]place)
452
distanceKeys = append(distanceKeys, dist)
454
places[dist][p.Name] = p
457
sort.Float64s(distanceKeys)
459
//create new category with no title
460
cat := reply.RegisterCategory("places", "", "", yl.Layout)
462
//This block creates a category with a title containing the number of results
463
length := len(answers.Businesses)
464
lenstring := fmt.Sprintf("%v results from Yelp", length)
465
cat = reply.RegisterCategory("places_with_len", lenstring, lenstring, yl.Layout)
468
rating := ratingFilter.Value(filterState)
470
//log.Printf("=== Yelp rating filter value: %v\n", rating)
471
distanceD := distanceFilter.Value(filterState)
473
//log.Printf("=== Yelp distance filter value: %v\n", distanceD)
474
if distunit == "mi" {
475
distanceD = distanceD * 1.60934
477
//log.Printf("=== Yelp distance after conversion: %v\n", distanceD)
482
excludedDistance := 0
484
for _, key := range distanceKeys {
485
for _, place := range places[key] {
487
//log.Printf("=== YELP res. idx: %v name: %q is_closed: %v, distance filter: %v\n", idx, place.Name, place.IsClosed, distanceD)
489
//if distance greater than sliderFilter value, don't show
490
if place.Rating < float32(rating) {
492
//log.Printf("=== YELP. EXCLUDED by place distance: %v, \n", distKm)
495
// don't show results for business that are closed when user filters by Open
496
if currentOpenFilter {
498
//log.Printf("=== YELP. EXCLUDED by IsClosed. name: %v", place.Name)
504
result := scopes.NewCategorisedResult(cat)
505
//add price to result if not 0 (unknown)
506
switch place.RestaurantsPriceRange {
508
result.Set("price", curSymb)
510
result.Set("price", curSymb+curSymb)
512
result.Set("price", curSymb+curSymb+curSymb)
514
result.Set("price", curSymb+curSymb+curSymb+curSymb)
517
//if price option filter(s), continue if no match
518
if currentPriceFilter.using {
520
switch place.RestaurantsPriceRange {
521
case 0: // indicates the business has no stated price range
522
if currentPriceFilter.unknown {
526
if currentPriceFilter.one {
530
if currentPriceFilter.two {
534
if currentPriceFilter.three {
538
if currentPriceFilter.four {
544
//log.Printf("=== YELP. EXCLUDED by price. name: %v", place.Name)
549
result.SetURI(place.Url)
550
//setup a point object of where we are
551
point1 := geo.NewPoint(loc.Latitude, loc.Longitude)
552
//and a point object of where the business is, iff yelp doesn't pass us 0,0
555
if place.Location.Coord.Latitude != 0 && place.Location.Coord.Longitude != 0 {
556
point2 := geo.NewPoint(place.Location.Coord.Latitude, place.Location.Coord.Longitude)
557
distKm = point1.GreatCircleDistance(point2)
558
if distunit == "mi" {
559
dist2 = fmt.Sprintf("%.2f %v", KmtoMi(distKm), distunit)
561
dist2 = fmt.Sprintf("%.2f %v", distKm, distunit)
564
//log.Printf("=== YELP. distance place: %v, filter km: %v\n", distKm, distanceD)
566
//if distance greater than sliderFilter value, don't show
567
if distKm > distanceD {
569
//log.Printf("=== YELP. EXCLUDED by place distance: %v, \n", distKm)
573
switch place.RestaurantsPriceRange {
575
priceLabel = ", " + priceOneLabel
577
priceLabel = ", " + priceTwoLabel
579
priceLabel = ", " + priceThreeLabel
581
priceLabel = ", " + priceFourLabel
583
attr := dist2 + priceLabel
584
atts := []attributeInfo{
585
attributeInfo{Value: attr},
586
attributeInfo{Value: fmt.Sprintf("(%v)", place.ReviewCount), Icon: place.RatingIcon},
589
result.Set("attributes", atts)
590
result.Set("summary", dist2)
591
result.SetTitle(place.Name)
592
if place.Image_url == "" {
593
result.SetArt(yl.Dir + "/images/yelp.png")
595
// replacing the ms with ls gives a higher resolution picture
596
result.SetArt(strings.Replace(place.Image_url, "ms.jpg", "o.jpg", 1))
598
var catstringlist []string
599
for _, thecat := range place.Cats {
600
catstringlist = append(catstringlist, thecat[0])
602
catstring := strings.Join(catstringlist, ", ")
603
result.Set("rate", place.Rating)
604
result.Set("address", catstring)
605
result.Set("phone", place.Phone)
606
result.Set("streetaddress1", place.Location.Display_address[0])
607
addstring := strings.Join(place.Location.Display_address[1:], " ")
608
result.Set("streetaddress2", addstring)
609
baseurl := fmt.Sprintf("http://m.here.com/#action=search¶ms={\"query\":\"%v %v\"}", place.Location.Display_address[0], addstring)
610
result.Set("coords", baseurl)
611
if err := reply.Push(result); err != nil {
612
log.Printf("==== YELP. error pushing result: %q\n", err.Error())
618
log.Printf("=== YELP. results: %v, pushed results: %v, excluded by rating: %v, price: %v, by open: %v, by distance: %v\n", len(answers.Businesses), pushed, excludedRating, excludedPrice, excludedOpen, excludedDistance)
620
cat := reply.RegisterCategory("noloc", "", "", errorTemplate)
621
result := scopes.NewCategorisedResult(cat)
622
result.SetTitle(gettext.Gettext("No location found"))
623
result.Set("subtitle", gettext.Gettext("Please make sure location is enabled"))
625
if err := reply.Push(result); err != nil {
249
query := q.QueryString()
250
loc := metadata.Location()
252
locale := metadata.Locale()
253
langstring := strings.Split(locale, "_")
254
log.Println("enweilocale", locale)
256
if langstring[1] == "US" {
259
minDistanceLabel := "0 km"
260
var maxDistance float64
261
maxDistance = 40 //km
262
var maxDistanceLabel string
263
maxDistanceLabel = "40 km"
264
if distunit == "mi" {
265
minDistanceLabel = "0 mi"
267
maxDistanceLabel = "25 mi"
270
var numResultsDefault float64
271
numResultsDefault = 40
272
numResultsFilter := scopes.NewValueSliderFilter("sliderResults", 10, 300, numResultsDefault,
273
scopes.ValueSliderLabels{
278
//Translators: This is the maximum number of Yelp results to display
279
numResultsFilter.Title = gettext.Gettext("Max results")
281
// distances in code always use km.
282
// slider gui uses mi (miles) in US
283
// When in US, the slider value is coverted to km on retrieval
284
var distanceDefault float64
286
distanceFilter := scopes.NewValueSliderFilter("distance", 0, maxDistance, distanceDefault,
287
scopes.ValueSliderLabels{
288
MinLabel: minDistanceLabel,
289
MaxLabel: maxDistanceLabel,
292
distanceFilter.Title = gettext.Gettext("Distance")
294
curSymb := C.GoString(C.localeconv().currency_symbol)
295
priceOneLabel := curSymb
296
priceTwoLabel := curSymb + curSymb
297
priceThreeLabel := curSymb + curSymb + curSymb
298
priceFourLabel := curSymb + curSymb + curSymb + curSymb
300
filterState := q.FilterState()
302
var ratingDefault float64
304
ratingFilter := scopes.NewValueSliderFilter("rating", 0, 5, ratingDefault,
305
scopes.ValueSliderLabels{
310
ratingFilter.Title = gettext.Gettext("Lowest rating")
312
priceFilter := scopes.NewOptionSelectorFilter("priceFilter", gettext.Gettext("Price range"), false)
313
priceFilter.AddOption("onePrice", priceOneLabel, false)
314
priceFilter.AddOption("twoPrice", priceTwoLabel, false)
315
priceFilter.AddOption("threePrice", priceThreeLabel, false)
316
priceFilter.AddOption("fourPrice", priceFourLabel, false)
318
//chnage to Switch filter when it moves from experimental namespace
319
openFilter := scopes.NewOptionSelectorFilter("openFilter", gettext.Gettext("Open"), false)
320
openFilter.AddOption("isOpen", gettext.Gettext("Is open"), false)
322
filters := []scopes.Filter{priceFilter, ratingFilter, openFilter, distanceFilter, numResultsFilter}
324
errP := reply.PushFilters(filters, q.FilterState())
326
return errors.New("yelp scope: cannot push filters. Error: " + errP.Error())
329
//change to Switch filter when it moves from experimental namespace
330
//currentOpenFilter := openFilter.IsOn(filterState)
331
currentOpenFilter := false
332
if openFilter.HasActiveOption(filterState) {
333
for _, option := range openFilter.ActiveOptions(filterState) {
334
if option == "isOpen" {
335
currentOpenFilter = true
340
var currentPriceFilter PriceFilter
341
currentPriceFilter.using = false
342
if priceFilter.HasActiveOption(filterState) {
343
for _, option := range priceFilter.ActiveOptions(filterState) {
346
currentPriceFilter.unknown = true
347
currentPriceFilter.using = true
349
currentPriceFilter.one = true
350
currentPriceFilter.using = true
352
currentPriceFilter.two = true
353
currentPriceFilter.using = true
355
currentPriceFilter.three = true
356
currentPriceFilter.using = true
358
currentPriceFilter.four = true
359
currentPriceFilter.using = true
364
rating := ratingFilter.Value(filterState)
365
log.Printf("=== Yelp rating filter value: %v\n", rating)
366
//setup locale, scope directory, layout, distance units, etc
367
yl.Dir = yl.Base.ScopeDirectory()
368
yl.Layout = yl.getLayout()
370
//get all the departments setup
371
rootDept, err := scopes.NewDepartment("", q, gettext.Gettext("Everything"))
375
var deptlist1 deptlist
376
err = yl.getDepts(&deptlist1)
377
for _, v := range deptlist1.Depts {
378
newdept, _ := scopes.NewDepartment(v.Label, q, gettext.Gettext(v.Name))
379
rootDept.AddSubdepartment(newdept)
380
if len(v.Subs) != 0 {
381
for _, s := range v.Subs {
382
newdept2, _ := scopes.NewDepartment(s.Label, q, gettext.Gettext(s.Name))
383
newdept.AddSubdepartment(newdept2)
387
reply.RegisterDepartments(rootDept)
389
numResults_f := numResultsFilter.Value(filterState)
391
//log.Printf("==== Yelp. num reuslts value: %v\n", numResults_f)
393
//log.Printf("=== YELP. num results: %v\n", numResults_f)
394
// determine number of times to call yelp api to get the number of results we want
395
numResults := int(numResults_f)
398
iters_f = numResults_f / 20
399
iters_s := strconv.FormatFloat(iters_f, 'f', 1, 64)
400
parts := strings.Split(iters_s, ".")
401
iters, _ = strconv.Atoi(parts[0])
402
itersDec, _ := strconv.Atoi(parts[1])
408
var token tokenResult
409
if err := yl.getAccessToken(&token); err != nil {
412
log.Println("AccessToken:", token.AccessToken)
413
yl.AccessToken = token.AccessToken*/
414
var answers yelpAnswer
416
//if loc is nil, there's nothing we can search for
418
distanceD := distanceFilter.Value(filterState)
420
//log.Printf("=== Yelp distance filter value: %v\n", distanceD)
421
if distunit == "mi" {
422
distanceD = distanceD * 1.60934
424
//log.Printf("=== Yelp distance after conversion: %v\n", distanceD)
425
//if price option filter(s), continue if no match
426
var priceFilterStr string
427
var priceFilterSet []string
428
if currentPriceFilter.using {
429
if currentPriceFilter.one {
430
priceFilterSet = append(priceFilterSet, "1")
432
if currentPriceFilter.two {
433
priceFilterSet = append(priceFilterSet, "2")
435
if currentPriceFilter.three {
436
priceFilterSet = append(priceFilterSet, "3")
438
if currentPriceFilter.four {
439
priceFilterSet = append(priceFilterSet, "4")
441
priceFilterStr = strings.Join(priceFilterSet, ", ")
444
queryParams := map[string]string{"latitude": strconv.FormatFloat(loc.Latitude, 'f', 4, 64), "longitude": strconv.FormatFloat(loc.Longitude, 'f', 4, 64), "locale": locale, "limit": strconv.Itoa(numResults), "offset": "0"}
445
if len(q.DepartmentID()) > 0 {
446
queryParams["categories"] = q.DepartmentID()
449
queryParams["term"] = query
451
if priceFilterStr != "" {
452
queryParams["pricing_filter"] = priceFilterStr
454
if currentOpenFilter {
455
queryParams["open_now_filter"] = "true"
457
//queryParams["sort_by"] = sortBy
458
// call to the web api
459
if err := yl.get(queryParams, "search?", &answers); err != nil {
464
//create new category with no title
465
cat := reply.RegisterCategory("places", "", "", yl.Layout)
467
//This block creates a category with a title containing the number of results
468
length := len(answers.Businesses)
469
lenstring := fmt.Sprintf("%v results from Yelp", length)
470
cat = reply.RegisterCategory("places_with_len", lenstring, lenstring, yl.Layout)
473
for _, p := range answers.Businesses {
474
log.Println("new result", p.Name)
475
if p.Rating < float32(rating) {
476
log.Println("rating ignore")
479
//setup a point object of where we are
480
point1 := geo.NewPoint(loc.Latitude, loc.Longitude)
481
//and a point object of where the business is, iff yelp doesn't pass us 0,0
484
if p.Location.Latitude != 0 && p.Location.Longitude != 0 {
485
point2 := geo.NewPoint(p.Location.Latitude, p.Location.Longitude)
486
distKm = point1.GreatCircleDistance(point2)
487
if distunit == "mi" {
488
dist2 = fmt.Sprintf("%.2f %v", KmtoMi(distKm), distunit)
490
dist2 = fmt.Sprintf("%.2f %v", distKm, distunit)
493
//if distance greater than sliderFilter value, don't show
494
if distKm > distanceD {
495
log.Printf("=== YELP. EXCLUDED by place distance: %v, \n", distKm)
499
result := scopes.NewCategorisedResult(cat)
501
result.Set("summary", dist2)
502
result.Set("id", p.Id)
505
imageName = "stars_large_0.png"
506
} else if p.Rating >= 1 && p.Rating < 2 {
507
imageName = "stars_large_1.png"
508
} else if p.Rating >= 2 && p.Rating < 2.5 {
509
imageName = "stars_large_2.png"
510
} else if p.Rating >= 2.5 && p.Rating < 3 {
511
imageName = "stars_large_2_half.png"
512
} else if p.Rating >= 3 && p.Rating < 3.5 {
513
imageName = "stars_large_3.png"
514
} else if p.Rating >= 3.5 && p.Rating < 4 {
515
imageName = "stars_large_3_half.png"
516
} else if p.Rating >= 4 && p.Rating < 4.5 {
517
imageName = "stars_large_4.png"
518
} else if p.Rating >= 4.5 && p.Rating < 5 {
519
imageName = "stars_large_4_half.png"
520
} else if p.Rating >= 5 {
521
imageName = "stars_large_5.png"
523
atts := []attributeInfo{
524
attributeInfo{Value: dist2},
525
attributeInfo{Value: fmt.Sprintf("(%v)", p.ReviewCount), Icon: yl.Dir + "/images/" + imageName},
527
result.Set("attributes", atts)
528
result.SetTitle(p.Name)
529
// if busi.ImageUrl == "" {
530
result.SetArt(yl.Dir + "/images/yelp.png")
532
// replacing the ms with ls gives a higher resolution picture
533
// result.SetArt(busi.ImageUrl)
535
var catstringlist []string
536
for _, thecat := range p.Cats {
537
catstringlist = append(catstringlist, thecat.Title)
540
catstring := strings.Join(catstringlist, ", ")
541
result.Set("rate", p.Rating)
542
result.Set("category", catstring)
544
log.Println("push result")
545
if err := reply.Push(result); err != nil {
546
log.Printf("==== YELP. error pushing result: %q\n", err.Error())
551
cat := reply.RegisterCategory("noloc", "", "", errorTemplate)
552
result := scopes.NewCategorisedResult(cat)
553
result.SetTitle(gettext.Gettext("No location found"))
554
result.Set("subtitle", gettext.Gettext("Please make sure location is enabled"))
556
if err := reply.Push(result); err != nil {
632
563
func (yl *YelpScope) Preview(result *scopes.Result, metadata *scopes.ActionMetadata, reply *scopes.PreviewReply, cancelled <-chan bool) error {
633
header := scopes.NewPreviewWidget("header", "header")
634
header.AddAttributeMapping("title", "title")
635
header.AddAttributeMapping("subtitle", "address")
638
if err := result.Get("rate", &rate); err != nil {
642
if err := result.Get("phone", &phone); err != nil {
645
if err := result.Get("coords", &coords); err != nil {
648
art := scopes.NewPreviewWidget("art", "image")
649
art.AddAttributeMapping("source", "art")
651
buttons := []actionInfo{
652
actionInfo{Id: "view", Label: gettext.Gettext("View on Yelp"), Icon: "/usr/share/icons/unity-icon-theme/places/svg/service-yelpplaces.svg"},
653
actionInfo{Id: "map", Label: gettext.Gettext("Show on Map"), Uri: coords},
654
actionInfo{Id: "phone", Label: gettext.Gettext("Call"), Uri: fmt.Sprintf("tel:///%v", phone), Icon: "theme://phone"},
656
reviews := []reviewInfo{
657
reviewInfo{Rating: rate, Author: ""},
660
ratings := scopes.NewPreviewWidget("ratings", "reviews")
661
ratings.AddAttributeValue("rating-icon-empty", yl.Dir+"/images/empty.png")
662
ratings.AddAttributeValue("rating-icon-half", yl.Dir+"/images/half.png")
663
ratings.AddAttributeValue("rating-icon-full", yl.Dir+"/images/full.png")
664
ratings.AddAttributeValue("reviews", reviews)
665
actions := scopes.NewPreviewWidget("actions", "actions")
666
actions.AddAttributeValue("actions", buttons)
668
addressheader := scopes.NewPreviewWidget("addressheader", "header")
669
addressheader.AddAttributeMapping("title", "streetaddress1")
670
addressheader.AddAttributeMapping("subtitle", "streetaddress2")
673
if err := result.Get("price", &price); err == nil {
674
priceWidget := scopes.NewPreviewWidget("price", "text")
675
priceWidget.AddAttributeValue("text", "<b>"+gettext.Gettext("Price")+"</b>: "+price)
676
return reply.PushWidgets(art, header, ratings, addressheader, priceWidget, actions)
679
return reply.PushWidgets(art, header, ratings, addressheader, actions)
564
header := scopes.NewPreviewWidget("header", "header")
565
header.AddAttributeMapping("title", "title")
566
header.AddAttributeMapping("subtitle", "category")
568
if err := result.Get("rate", &rate); err != nil {
572
if err := result.Get("id", &id); err != nil {
576
if err := yl.get(map[string]string{}, id, &busi); err != nil {
579
art := scopes.NewPreviewWidget("art", "gallery")
580
art.AddAttributeValue("sources", busi.Photos)
582
result.Set("streetaddress1", busi.Location.Address1)
584
if busi.Location.Address2 != "" {
585
addstring += busi.Location.Address2
587
if busi.Location.Address3 != "" {
589
addstring += busi.Location.Address3
591
if busi.Location.City != "" {
593
addstring += busi.Location.City
595
if busi.Location.Zip != "" {
597
addstring += busi.Location.Zip
599
if busi.Location.Country != "" {
601
addstring += busi.Location.Country
604
baseurl := fmt.Sprintf("http://m.here.com/#action=search¶ms={\"query\":\"%v %v\"}", busi.Location.Address1, addstring)
605
var buttons []actionInfo
606
if busi.Phone == "" {
607
buttons = []actionInfo{
608
actionInfo{Id: "view", Label: gettext.Gettext("View on Yelp"), Icon: "/usr/share/icons/unity-icon-theme/places/svg/service-yelpplaces.svg"},
609
actionInfo{Id: "map", Label: gettext.Gettext("Show on Map"), Uri: baseurl},
612
buttons = []actionInfo{
613
actionInfo{Id: "view", Label: gettext.Gettext("View on Yelp"), Icon: "/usr/share/icons/unity-icon-theme/places/svg/service-yelpplaces.svg"},
614
actionInfo{Id: "map", Label: gettext.Gettext("Show on Map"), Uri: baseurl},
615
actionInfo{Id: "phone", Label: gettext.Gettext("Call"), Uri: fmt.Sprintf("tel:///%v", busi.Phone), Icon: "theme://phone"},
620
if err := yl.get(map[string]string{}, id + "/reviews", &res); err != nil {
623
reviews := []reviewInfo{}
624
for _, review := range res.Reviews {
625
reviews = append(reviews, reviewInfo{Rating: review.Rating, Author: "", Review: review.Text})
628
ratings := scopes.NewPreviewWidget("ratings", "reviews")
629
ratings.AddAttributeValue("rating-icon-empty", yl.Dir+"/images/empty.png")
630
ratings.AddAttributeValue("rating-icon-half", yl.Dir+"/images/half.png")
631
ratings.AddAttributeValue("rating-icon-full", yl.Dir+"/images/full.png")
632
ratings.AddAttributeValue("reviews", reviews)
634
actions := scopes.NewPreviewWidget("actions", "actions")
635
actions.AddAttributeValue("actions", buttons)
637
addressheader := scopes.NewPreviewWidget("addressheader", "header")
638
addressheader.AddAttributeValue("title", busi.Location.Address1)
639
addressheader.AddAttributeValue("subtitle", addstring)
641
return reply.PushWidgets(art, header, ratings, addressheader, actions)
645
var clientsecret = ""
683
log.Println("Starting yelp scope")
685
BaseURI: "http://api.yelp.com/v2/search?",
686
ConsumerKey: "2lUxf_XzPiZ-e8jJfn4JbA",
687
Token: "yhS5hXf3ES_ChYsg_FWFqmNoDucBUu-0",
688
ConsumerSecret: "rQvoNZMcf3-CG-4OgIA2McFynLU",
689
TokenSecret: "gFgW9giUF-sMqYfFEVa2JIJTCfY",
693
if err := scopes.Run(scope); err != nil {
648
log.Println("Starting yelp scope")
650
BaseURI: "https://api.yelp.com/v3/business/",
652
AccessToken: "",//"9DSPv4a_pu4Qyt9FQe1jDfL-jzO_4g5s6HKpwTxHR2sHgzfzPIHb0XbH2xYQ5n2ojKxxJji8kGNKodFP60l4sKtjUZLu8P59l8Pzeof9aaWDat96lm1VzdhkOfQ-V3Yx",
653
ClientSecret: clientsecret,
657
if err := scopes.Run(scope); err != nil {