This repository has been archived by the owner on Jul 15, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.go
118 lines (100 loc) · 2.7 KB
/
app.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main
import (
"bytes"
"errors"
"fmt"
"os"
"github.com/urfave/cli"
)
var (
// ErrMissingCSVFilePath is returned when missing filepath flag in case datasource=csv
ErrMissingCSVFilePath = errors.New("missing filepath")
)
func configureApp() *cli.App {
app := cli.NewApp()
app.Version = "0.0.1"
app.Name = "closest_listings"
app.Usage = "Find closest listings from a specific coordinate. Data is fetched from csv"
app.Flags = []cli.Flag{
cli.Int64Flag{
Name: "top",
Value: 5,
Usage: "number of places to print out",
},
cli.StringFlag{
Name: "datasource, d",
Value: "csv",
Usage: "where to get list of coordinates, could be from csv, maybe sql in the future",
},
cli.StringFlag{
Name: "filepath",
Usage: "path to csv file, only used when datasource=csv",
},
cli.Float64Flag{
Name: "lat",
Value: 51.925146,
Usage: "Longitude of the initial place",
},
cli.Float64Flag{
Name: "long",
Value: 4.478617,
Usage: "Longitude of the initial place",
},
}
app.Action = applogic
return app
}
func applogic(c *cli.Context) error {
top := c.Int64("top")
fromCoord := Coordinate{
Lat: c.Float64("lat"),
Lng: c.Float64("long"),
}
datasource, err := getDataSource(c)
if err != nil {
return err
}
go datasource.Fetch()
nearestPlaces := findClosestListings(top, fromCoord, datasource.DataChan())
if err = datasource.FetchError(); err != nil {
return err
}
b := formatOutput(top, fromCoord, nearestPlaces)
b.WriteTo(os.Stdout)
return nil
}
func findClosestListings(top int64, from Coordinate, input chan Listing) []Listing {
sortedList := NewSortedList(top)
for listing := range input {
distance := from.GreatCircleDistance(listing.Coordinate)
sortedList.Insert(distance, listing)
}
sortedResults := sortedList.TopWithScore(-1)
clostestPlaces := make([]Listing, len(sortedResults))
for i, el := range sortedResults {
clostestPlaces[i] = el.Value.(Listing)
clostestPlaces[i].Distance = el.Score
}
return clostestPlaces
}
func formatOutput(top int64, fromCoord Coordinate, clostestPlaces []Listing) *bytes.Buffer {
output := &bytes.Buffer{}
fmt.Fprintf(output, "Top %d closest listings from coordinate %s:\n", top, fromCoord.String())
for i, place := range clostestPlaces {
fmt.Fprintf(output, "%d. id=%s coord=%s distance=%v\n", i+1, place.ID, place.Coordinate.String(), place.Distance)
}
return output
}
func getDataSource(c *cli.Context) (ListingsSource, error) {
source := c.String("datasource")
switch source {
case "csv":
filepath := c.String("filepath")
if filepath == "" {
return nil, ErrMissingCSVFilePath
}
return NewCSVSource(filepath), nil
default:
return nil, fmt.Errorf("datasource %s is unknown", source)
}
}