diff options
author | Sergey Grebenshchikov <sgreben@gmail.com> | 2018-03-22 02:15:42 +0100 |
---|---|---|
committer | Sergey Grebenshchikov <sgreben@gmail.com> | 2018-03-22 10:04:51 +0100 |
commit | a49891bdb657d7cbb2521f305251cda1f00359d3 (patch) | |
tree | bb438378e68025641c54d0b2f2dfdcc73c33a4b0 /cmd |
Initial commit1.0.0
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/jp/bar.go | 43 | ||||
-rw-r--r-- | cmd/jp/flag.go | 26 | ||||
-rw-r--r-- | cmd/jp/line.go | 52 | ||||
-rw-r--r-- | cmd/jp/main.go | 110 | ||||
-rw-r--r-- | cmd/jp/split.go | 20 |
5 files changed, 251 insertions, 0 deletions
diff --git a/cmd/jp/bar.go b/cmd/jp/bar.go new file mode 100644 index 0000000..be0c230 --- /dev/null +++ b/cmd/jp/bar.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "reflect" + + "github.com/sgreben/jp/pkg/jp" + "github.com/sgreben/jp/pkg/jp/primitives" +) + +func barPlotData(xvv, yvv [][]reflect.Value) (x []string, y []float64) { + for _, xv := range xvv { + for i := range xv { + x = append(x, fmt.Sprint(xv[i].Interface())) + } + } + for _, yv := range yvv { + for i := range yv { + yvi, ok := yv[i].Interface().(float64) + if ok { + y = append(y, yvi) + } + } + } + return +} + +func barPlot(xvv, yvv [][]reflect.Value, box primitives.Box) string { + groups, y := barPlotData(xvv, yvv) + chart := jp.NewBarChart(box.Width, box.Height) + data := new(jp.DataTable) + if len(groups) != len(y) { + for i := range y { + data.AddColumn(fmt.Sprint(i)) + } + } else { + for _, g := range groups { + data.AddColumn(g) + } + } + data.AddRow(y...) + return chart.Draw(data) +} diff --git a/cmd/jp/flag.go b/cmd/jp/flag.go new file mode 100644 index 0000000..533b35d --- /dev/null +++ b/cmd/jp/flag.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "strings" +) + +type enumVar struct { + Choices []string // The acceptable choices the user may pass to the flag + Value string // the current value of the flag +} + +// Set implements the flag.Value interface. +func (so *enumVar) Set(v string) error { + for _, c := range so.Choices { + if c == v { + so.Value = v + return nil + } + } + return fmt.Errorf("invalid choice; must be one of %s", strings.Join(so.Choices, ",")) +} + +func (so *enumVar) String() string { + return so.Value +} diff --git a/cmd/jp/line.go b/cmd/jp/line.go new file mode 100644 index 0000000..6b75a8f --- /dev/null +++ b/cmd/jp/line.go @@ -0,0 +1,52 @@ +package main + +import ( + "reflect" + + "github.com/sgreben/jp/pkg/jp" + "github.com/sgreben/jp/pkg/jp/primitives" +) + +func linePlotData(xvv, yvv [][]reflect.Value) (x, y []float64) { + for _, xv := range xvv { + for i := range xv { + xvi, ok := xv[i].Interface().(float64) + if ok { + x = append(x, xvi) + } + } + } + for _, yv := range yvv { + for i := range yv { + yvi, ok := yv[i].Interface().(float64) + if ok { + y = append(y, yvi) + } + } + } + return +} + +func linePlot(xvv, yvv [][]reflect.Value, box primitives.Box) string { + x, y := linePlotData(xvv, yvv) + chart := jp.NewLineChart(box.Width, box.Height) + data := new(jp.DataTable) + data.AddColumn("x") + data.AddColumn("y") + n := len(x) + if len(y) > n { + n = len(y) + } + // If no valid xs are given, use the indices as x values. + if len(x) == 0 { + x = make([]float64, len(y)) + for i := 0; i < len(y); i++ { + x[i] = float64(i) + } + } + for i := 0; i < n; i++ { + data.AddRow(x[i%len(x)], y[i%len(y)]) + } + chart.Symbol = "█" + return chart.Draw(data) +} diff --git a/cmd/jp/main.go b/cmd/jp/main.go new file mode 100644 index 0000000..c603f68 --- /dev/null +++ b/cmd/jp/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "os" + "reflect" + + "github.com/sgreben/jp/pkg/jp/primitives" + "github.com/sgreben/jp/pkg/terminal" + + "github.com/sgreben/jp/pkg/jsonpath" +) + +type configuration struct { + Box primitives.Box + X string + Y string + XY string + PlotType enumVar +} + +const plotTypeLine = "line" +const plotTypeBar = "bar" +const plotTypeScatter = "scatter" +const plotTypeHist = "hist" + +var config = configuration{ + PlotType: enumVar{ + Value: plotTypeLine, + Choices: []string{ + plotTypeLine, + plotTypeBar, + }, + }, +} + +var xPattern *jsonpath.JSONPath +var yPattern *jsonpath.JSONPath +var xyPattern *jsonpath.JSONPath + +func init() { + flag.Var(&config.PlotType, "type", fmt.Sprintf("Plot type. One of %v", config.PlotType.Choices)) + flag.StringVar(&config.X, "x", "", "x values (JSONPath expression)") + flag.StringVar(&config.Y, "y", "", "y values (JSONPath expression)") + flag.StringVar(&config.XY, "xy", "", "x,y value pairs (JSONPath expression). Overrides -x and -y if given.") + flag.IntVar(&config.Box.Width, "width", 0, "Plot width (default 0 (auto))") + flag.IntVar(&config.Box.Height, "height", 0, "Plot height (default 0 (auto))") + flag.Parse() + log.SetOutput(os.Stderr) + + var err error + xPattern = jsonpath.New("x") + err = xPattern.Parse(fmt.Sprintf("{%s}", config.X)) + if err != nil { + log.Fatal(err) + } + yPattern = jsonpath.New("y") + err = yPattern.Parse(fmt.Sprintf("{%s}", config.Y)) + if err != nil { + log.Fatal(err) + } + if config.XY != "" { + xyPattern = jsonpath.New("xy") + err = xyPattern.Parse(fmt.Sprintf("{%s}", config.XY)) + if err != nil { + log.Fatal(err) + } + } + if config.Box.Width == 0 { + config.Box.Width = terminal.Width() + } + if config.Box.Height == 0 { + config.Box.Height = terminal.Height() - 1 + } +} + +func match(in interface{}, p *jsonpath.JSONPath) [][]reflect.Value { + out, err := p.FindResults(in) + if err != nil { + log.Println(err) + } + return out +} + +func main() { + var in interface{} + dec := json.NewDecoder(os.Stdin) + err := dec.Decode(&in) + if err != nil { + log.Println(err) + } + fmt.Println() + var x, y [][]reflect.Value + if xyPattern != nil { + x, y = split(match(in, xyPattern)) + } else { + x = match(in, xPattern) + y = match(in, yPattern) + } + switch config.PlotType.Value { + case plotTypeLine: + fmt.Print(linePlot(x, y, config.Box)) + case plotTypeBar: + fmt.Print(barPlot(x, y, config.Box)) + } + +} diff --git a/cmd/jp/split.go b/cmd/jp/split.go new file mode 100644 index 0000000..4f36825 --- /dev/null +++ b/cmd/jp/split.go @@ -0,0 +1,20 @@ +package main + +import "reflect" + +func flatten(in [][]reflect.Value) (out []reflect.Value) { + for _, a := range in { + for i := range a { + out = append(out, a[i]) + } + } + return +} + +func split(in [][]reflect.Value) (x, y [][]reflect.Value) { + flat := flatten(in) + n := len(flat) + x = [][]reflect.Value{flat[:n/2]} + y = [][]reflect.Value{flat[n/2:]} + return +} |