1

Changing error handling, adding computeRecordPopularity

This commit is contained in:
Alex Yatskov 2015-03-24 17:29:24 +09:00
parent ad516fccd5
commit b5eea20b8d
3 changed files with 136 additions and 44 deletions

View File

@ -47,8 +47,7 @@ func getCategories(rw http.ResponseWriter, req *http.Request) {
rows, err := db.Query("SELECT * FROM categories") rows, err := db.Query("SELECT * FROM categories")
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
defer rows.Close() defer rows.Close()
@ -60,22 +59,19 @@ func getCategories(rw http.ResponseWriter, req *http.Request) {
) )
if err := rows.Scan(&description, &id); err != nil { if err := rows.Scan(&description, &id); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
categories = append(categories, Category{description, id}) categories = append(categories, Category{description, id})
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
js, err := json.Marshal(categories) js, err := json.Marshal(categories)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Content-Type", "application/json")
@ -104,20 +100,17 @@ func addCategory(rw http.ResponseWriter, req *http.Request) {
if len(request.Description) > 0 { if len(request.Description) > 0 {
result, err := db.Exec("INSERT INTO categories(description) VALUES(?)", request.Description) result, err := db.Exec("INSERT INTO categories(description) VALUES(?)", request.Description)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
insertId, err := result.LastInsertId() insertId, err := result.LastInsertId()
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
affectedRows, err := result.RowsAffected() affectedRows, err := result.RowsAffected()
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
response.Success = affectedRows > 0 response.Success = affectedRows > 0
@ -126,8 +119,7 @@ func addCategory(rw http.ResponseWriter, req *http.Request) {
js, err := json.Marshal(response) js, err := json.Marshal(response)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Content-Type", "application/json")
@ -151,20 +143,17 @@ func removeCategory(rw http.ResponseWriter, req *http.Request) {
result, err := db.Exec("DELETE FROM categories WHERE id = (?)", request.Id) result, err := db.Exec("DELETE FROM categories WHERE id = (?)", request.Id)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
affectedRows, err := result.RowsAffected() affectedRows, err := result.RowsAffected()
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
js, err := json.Marshal(Response{affectedRows > 0}) js, err := json.Marshal(Response{affectedRows > 0})
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) log.Fatal(err)
return
} }
rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Content-Type", "application/json")

View File

@ -22,32 +22,42 @@
package main package main
type Context struct {
hasPosition bool
latitude float64
longitude float64
profile Features
walkingDist float64
}
type Projection struct { type Projection struct {
sample float32 sample float64
stats RecordStats stats RecordStats
} }
type Features map[string]float32 type Features map[interface{}]float64
type RecordStats struct { type RecordStats struct {
compatibility float32 compatibility float64
count int count int
} }
type Range struct { type Range struct {
max float32 max float64
min float32 min float64
} }
type Record struct { type Record struct {
accessCount int accessCount int
compatibility float32 compatibility float64
distanceToStn float32 distanceToStn float64
distanceToUser float32 distanceToUser float64
features Features features Features
id int id int
latitude float64
longitude float64
name string name string
score float32 score float64
} }
type Records []Record type Records []Record

119
util.go
View File

@ -22,10 +22,15 @@
package main package main
import "sort" import (
"github.com/kellydunn/golang-geo"
"log"
"math"
"sort"
)
func innerProduct(features1 Features, features2 Features) float32 { func innerProduct(features1 Features, features2 Features) float64 {
var result float32 var result float64
for key, value1 := range features1 { for key, value1 := range features1 {
value2, _ := features2[key] value2, _ := features2[key]
result += value1 * value2 result += value1 * value2
@ -34,7 +39,7 @@ func innerProduct(features1 Features, features2 Features) float32 {
return result return result
} }
func walkMatches(records Records, features Features, minScore float32, callback func(Record, float32)) { func walkMatches(records Records, features Features, minScore float64, callback func(Record, float64)) {
for _, record := range records { for _, record := range records {
if score := innerProduct(features, record.features); score >= minScore { if score := innerProduct(features, record.features); score >= minScore {
callback(record, score) callback(record, score)
@ -42,9 +47,9 @@ func walkMatches(records Records, features Features, minScore float32, callback
} }
} }
func statRecords(records Records, features Features, minScore float32) RecordStats { func statRecords(records Records, features Features, minScore float64) RecordStats {
var stats RecordStats var stats RecordStats
walkMatches(records, features, minScore, func(record Record, score float32) { walkMatches(records, features, minScore, func(record Record, score float64) {
stats.compatibility += record.compatibility stats.compatibility += record.compatibility
stats.count++ stats.count++
}) })
@ -52,11 +57,11 @@ func statRecords(records Records, features Features, minScore float32) RecordSta
return stats return stats
} }
func stepRange(rng Range, steps int, callback func(float32)) { func stepRange(rng Range, steps int, callback func(float64)) {
stepSize := (rng.max - rng.min) / float32(steps) stepSize := (rng.max - rng.min) / float64(steps)
for i := 0; i < steps; i++ { for i := 0; i < steps; i++ {
stepMax := rng.max - stepSize*float32(i) stepMax := rng.max - stepSize*float64(i)
stepMin := stepMax - stepSize stepMin := stepMax - stepSize
stepMid := (stepMin + stepMax) / 2 stepMid := (stepMin + stepMax) / 2
@ -64,24 +69,24 @@ func stepRange(rng Range, steps int, callback func(float32)) {
} }
} }
func findRecords(records Records, features Features, minScore float32) { func findRecords(records Records, features Features, minScore float64) {
var foundRecords Records var foundRecords Records
walkMatches(records, features, minScore, func(record Record, score float32) { walkMatches(records, features, minScore, func(record Record, score float64) {
foundRecords = append(foundRecords, record) foundRecords = append(foundRecords, record)
}) })
sort.Sort(foundRecords) sort.Sort(foundRecords)
} }
func project(records Records, features Features, featureName string, minScore float32, rng Range, steps int) []Projection { func project(records Records, features Features, featureName string, minScore float64, rng Range, steps int) []Projection {
sampleFeatures := make(Features) sampleFeatures := make(Features)
for key, value := range features { for key, value := range features {
sampleFeatures[key] = value sampleFeatures[key] = value
} }
var projection []Projection var projection []Projection
stepRange(rng, steps, func(sample float32) { stepRange(rng, steps, func(sample float64) {
sampleFeatures[featureName] = sample sampleFeatures[featureName] = sample
stats := statRecords(records, sampleFeatures, minScore) stats := statRecords(records, sampleFeatures, minScore)
projection = append(projection, Projection{sample: sample, stats: stats}) projection = append(projection, Projection{sample: sample, stats: stats})
@ -89,3 +94,91 @@ func project(records Records, features Features, featureName string, minScore fl
return projection return projection
} }
func computeRecordGeo(records Records, context Context) {
distUserMin := math.MaxFloat64
distUserMax := 0.0
for _, record := range records {
if context.hasPosition {
userPoint := geo.NewPoint(context.latitude, context.longitude)
recordPoint := geo.NewPoint(record.latitude, context.longitude)
record.distanceToUser = userPoint.GreatCircleDistance(recordPoint)
}
if record.distanceToUser < distUserMin {
distUserMin = record.distanceToUser
}
if record.distanceToUser > distUserMax {
distUserMax = record.distanceToUser
}
}
distUserRange := distUserMax - distUserMin
for _, record := range records {
nearby := -((record.distanceToUser-distUserMin)/distUserRange - 0.5) * 2.0
accessible := 1.0 - (record.distanceToStn / context.walkingDist)
if accessible < -1.0 {
accessible = 1.0
} else if accessible > 1.0 {
accessible = 1.0
}
record.features["nearby"] = nearby
record.features["accessible"] = accessible
}
}
func computeRecordPopularity(records Records, context Context) {
for _, record := range records {
historyRows, err := db.Query("SELECT id FROM history WHERE reviewId = (?)", record.id)
if err != nil {
log.Fatal(err)
}
var groupSum float64
var groupCount int
for historyRows.Next() {
var historyId int
if err := historyRows.Scan(&historyId); err != nil {
log.Fatal(err)
}
groupRows, err := db.Query("SELECT categoryId, categoryValue FROM historyGroups WHERE historyId = (?)", historyId)
if err != nil {
log.Fatal(err)
}
recordProfile := make(Features)
for groupRows.Next() {
var categoryId int
var categoryValue float64
if err := groupRows.Scan(&categoryId, &categoryValue); err != nil {
log.Fatal(err)
}
recordProfile[categoryId] = categoryValue
}
if err := groupRows.Err(); err != nil {
log.Fatal(err)
}
groupSum += innerProduct(recordProfile, context.profile)
groupCount++
}
if err := historyRows.Err(); err != nil {
log.Fatal(err)
}
var compatibility float64
if groupCount > 0 {
compatibility = groupSum / float64(groupCount)
}
record.features["compatibility"] = compatibility
}
}