From f05d75b8eae03c6fcf12f1dc8735720f5538f6e5 Mon Sep 17 00:00:00 2001 From: Haytham AbuelFutuh Date: Thu, 4 Jun 2020 13:58:34 -0700 Subject: [PATCH] Clean up timeline command for now --- cmd/testdata/expected.png | Bin 62189 -> 0 bytes cmd/testdata/listNodeExecutions.pb | 1 - cmd/timeline.go | 197 ------------- cmd/timeline_test.go | 178 ------------ graph/timeline_chart.go | 436 ----------------------------- 5 files changed, 812 deletions(-) delete mode 100644 cmd/testdata/expected.png delete mode 100755 cmd/testdata/listNodeExecutions.pb delete mode 100644 cmd/timeline.go delete mode 100644 cmd/timeline_test.go delete mode 100644 graph/timeline_chart.go diff --git a/cmd/testdata/expected.png b/cmd/testdata/expected.png deleted file mode 100644 index 76c2086c5cf07908bcfad224daf36306c64bc271..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62189 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 0 { - // return fmt.Errorf(result.Message) - //} - // - ////save to file - //if flags.OutputPath != nil { - // return result.Context.SavePNG(*flags.OutputPath) - //} - // -} diff --git a/cmd/timeline_test.go b/cmd/timeline_test.go deleted file mode 100644 index 047d12be..00000000 --- a/cmd/timeline_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package cmd - -import ( - "bytes" - "context" - "flag" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/golang/protobuf/jsonpb" - - "github.com/stretchr/testify/mock" - - adminIdl "github.com/lyft/flyteidl/gen/pb-go/flyteidl/admin" - coreIdl "github.com/lyft/flyteidl/gen/pb-go/flyteidl/core" - - "github.com/lyft/flyteidl/clients/go/admin" - "github.com/lyft/flytestdlib/config" - "github.com/lyft/flytestdlib/config/viper" - - "github.com/lyft/flyteidl/clients/go/admin/mocks" - - "github.com/stretchr/testify/assert" -) - -var update = flag.Bool("update", false, "Updates testdata") - -func refStr(s string) *string { - return &s -} - -func copyFile(src, dst string) error { - sourceFileStat, err := os.Stat(src) - if err != nil { - return err - } - - if !sourceFileStat.Mode().IsRegular() { - return fmt.Errorf("%s is not a regular file", src) - } - - source, err := os.Open(src) - if err != nil { - return err - } - - destination, err := os.Create(dst) - if err != nil { - err2 := source.Close() - if err2 != nil { - return config.ErrorCollection([]error{err, err2}) - } - - return err - } - - errs := make([]error, 0) - _, err = io.Copy(destination, source) - if err != nil { - errs = append(errs, err) - } - - err2 := source.Close() - if err2 != nil { - errs = append(errs, err2) - } - - err3 := destination.Close() - if err3 != nil { - errs = append(errs, err3) - } - - if len(errs) > 0 { - return config.ErrorCollection(errs) - } - - return nil -} - -func Test_updateAdminResponse(t *testing.T) { - if !*update { - t.SkipNow() - } - - accessor := viper.NewAccessor(config.Options{ - SearchPaths: []string{filepath.Join("testdata", "config.yaml")}, - }) - - ctx := context.Background() - assert.NoError(t, accessor.UpdateConfig(ctx)) - c := admin.InitializeAdminClient(ctx, *admin.GetConfig(ctx)) - resp, err := c.ListNodeExecutions(ctx, &adminIdl.NodeExecutionListRequest{ - WorkflowExecutionId: &coreIdl.WorkflowExecutionIdentifier{ - Project: "priceoptimizeroffline", - Domain: "production", - Name: "eqwdb3jwg7", - }, - Limit: 100, - }) - - assert.NoError(t, err) - - if err != nil { - t.FailNow() - } - - m := &jsonpb.Marshaler{} - var buf bytes.Buffer - err = m.Marshal(&buf, resp) - assert.NoError(t, err) - assert.NoError(t, ioutil.WriteFile(filepath.Join("testdata", "listNodeExecutions.pb"), buf.Bytes(), os.ModePerm)) -} - -func Test_visualizeTimeline(t *testing.T) { - ctx := context.Background() - - respBytes, err := ioutil.ReadFile(filepath.Join("testdata", "listNodeExecutions.pb")) - assert.NoError(t, err) - - resp := &adminIdl.NodeExecutionList{} - assert.NoError(t, jsonpb.Unmarshal(bytes.NewReader(respBytes), resp)) - - m := &mocks.AdminServiceClient{} - m.OnListNodeExecutionsMatch(mock.Anything, mock.Anything, mock.Anything).Return(resp, nil) - assert.NoError(t, visualizeTimeline(ctx, m, timelineFlags{ - persistentFlags: persistentFlags{ - Project: refStr("priceoptimizeroffline"), - Domain: refStr("production"), - }, - ExecutionName: refStr("eqwdb3jwg7"), - })) -} - -func Test_visualizeTimeline_test_output(t *testing.T) { - ctx := context.Background() - - respBytes, err := ioutil.ReadFile(filepath.Join("testdata", "listNodeExecutions.pb")) - assert.NoError(t, err) - - resp := &adminIdl.NodeExecutionList{} - assert.NoError(t, jsonpb.Unmarshal(bytes.NewReader(respBytes), resp)) - - tmpLoc, err := ioutil.TempFile(os.TempDir(), "visualize_time_line.png") - assert.NoError(t, err) - assert.NoError(t, tmpLoc.Close()) - - m := &mocks.AdminServiceClient{} - m.OnListNodeExecutionsMatch(mock.Anything, mock.Anything, mock.Anything).Return(resp, nil) - assert.NoError(t, visualizeTimeline(ctx, m, timelineFlags{ - persistentFlags: persistentFlags{ - Project: refStr("priceoptimizeroffline"), - Domain: refStr("production"), - }, - ExecutionName: refStr("eqwdb3jwg7"), - OutputPath: refStr(tmpLoc.Name()), - })) - - expectedPath := filepath.Join("testdata", "expected.png") - if *update { - assert.NoError(t, copyFile(tmpLoc.Name(), expectedPath)) - } - - expectedBytes, err := ioutil.ReadFile(expectedPath) - assert.NoError(t, err) - - actualBytes, err := ioutil.ReadFile(tmpLoc.Name()) - assert.NoError(t, err) - - if assert.Equal(t, expectedBytes, actualBytes) { - assert.NoError(t, os.Remove(tmpLoc.Name())) - } else { - t.Logf("Files are different, expected file [%v] vs actual file [%v]", expectedPath, tmpLoc.Name()) - } -} diff --git a/graph/timeline_chart.go b/graph/timeline_chart.go deleted file mode 100644 index 11da5973..00000000 --- a/graph/timeline_chart.go +++ /dev/null @@ -1,436 +0,0 @@ -package graph - -import ( - "errors" - "fmt" - "io" - "math" - - chart "github.com/wcharczuk/go-chart" - - "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/seq" - util "github.com/wcharczuk/go-chart/util" -) - -type BarSection struct { - StartValue float64 - Length float64 -} - -// BarSections is an array of Value. -type BarSections []BarSection - -//// BarSections returns the values. -//func (vs BarSections) Values() []float64 { -// values := make([]float64, len(vs)) -// for index, v := range vs { -// values[index] = v.Length -// } -// -// return values -//} - -//// ValuesNormalized returns normalized values. -//func (vs BarSections) ValuesNormalized() []float64 { -// return util.Math.Normalize(vs.Values()...) -//} - -// Normalize returns the values normalized. -func (vs BarSections) Normalize(minValue, maxValue float64) []BarSection { - var output []BarSection - for _, v := range vs { - if v.Length > 0 { - output = append(output, BarSection{ - StartValue: util.Math.RoundUp((v.StartValue-minValue)/(maxValue-minValue), 0.0001), - Length: util.Math.RoundUp(v.Length/(maxValue-minValue), 0.0001), - }) - } - } - - return output -} - -// Bar is a bar within a TimelineChart. -type Bar struct { - Name string - Style chart.Style - Sections BarSections -} - -// GetHeight returns the width of the bar. -func (sb Bar) GetHeight() int { - return 15 -} - -// TimelineChart is a chart that draws sections of a bar based on percentages. -type TimelineChart struct { - Title string - TitleStyle chart.Style - - ColorPalette chart.ColorPalette - - Width int - Height int - DPI float64 - - Background chart.Style - Canvas chart.Style - - XAxis chart.Style - YAxis chart.Style - - BarSpacing int - - Font *truetype.Font - defaultFont *truetype.Font - - Bars []Bar - Elements []chart.Renderable -} - -// GetDPI returns the dpi for the chart. -func (sbc TimelineChart) GetDPI(defaults ...float64) float64 { - if sbc.DPI == 0 { - if len(defaults) > 0 { - return defaults[0] - } - return chart.DefaultDPI - } - return sbc.DPI -} - -// GetFont returns the text font. -func (sbc TimelineChart) GetFont() *truetype.Font { - if sbc.Font == nil { - return sbc.defaultFont - } - return sbc.Font -} - -// GetHeight returns the chart width or the default value. -func (sbc TimelineChart) GetWidth() int { - if sbc.Width == 0 { - return chart.DefaultChartWidth - } - return sbc.Width -} - -// GetHeight returns the chart height or the default value. -func (sbc TimelineChart) GetHeight() int { - if sbc.Height == 0 { - return chart.DefaultChartHeight - } - return sbc.Height -} - -// GetBarSpacing returns the spacing between bars. -func (sbc TimelineChart) GetBarSpacing() int { - if sbc.BarSpacing == 0 { - return 100 - } - return sbc.BarSpacing -} - -// Render renders the chart with the given renderer to the given io.Writer. -func (sbc TimelineChart) Render(rp chart.RendererProvider, w io.Writer) error { - if len(sbc.Bars) == 0 { - return errors.New("please provide at least one bar") - } - - r, err := rp(sbc.GetWidth(), sbc.GetHeight()) - if err != nil { - return err - } - - if sbc.Font == nil { - defaultFont, err := chart.GetDefaultFont() - if err != nil { - return err - } - sbc.defaultFont = defaultFont - } - r.SetDPI(sbc.GetDPI(chart.DefaultDPI)) - - canvasBox := sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox()) - sbc.drawCanvas(r, canvasBox) - sbc.drawBars(r, canvasBox) - sbc.drawXAxis(r, canvasBox) - sbc.drawYAxis(r, canvasBox) - - sbc.drawTitle(r) - for _, a := range sbc.Elements { - a(r, canvasBox, sbc.styleDefaultsElements()) - } - - return r.Save(w) -} - -func (sbc TimelineChart) drawCanvas(r chart.Renderer, canvasBox chart.Box) { - chart.Draw.Box(r, canvasBox, sbc.getCanvasStyle()) -} - -func (sbc TimelineChart) drawBars(r chart.Renderer, canvasBox chart.Box) { - yoffset := canvasBox.Bottom - for _, bar := range sbc.Bars { - sbc.drawBar(r, canvasBox, sbc.getMinValue(), sbc.getMaxValue(), yoffset, bar) - yoffset -= sbc.GetBarSpacing() - bar.GetHeight() - } -} - -func (sbc TimelineChart) getMaxValue() float64 { - maxValue := float64(0) - for _, bar := range sbc.Bars { - for _, section := range bar.Sections { - maxValue = math.Max(maxValue, section.Length+section.StartValue) - } - } - - return maxValue -} - -func (sbc TimelineChart) getMinValue() float64 { - maxValue := math.MaxFloat64 - for _, bar := range sbc.Bars { - for _, section := range bar.Sections { - maxValue = math.Min(maxValue, section.StartValue) - } - } - - return maxValue -} - -func (sbc TimelineChart) drawBar(r chart.Renderer, canvasBox chart.Box, minValue, maxValue float64, yoffset int, bar Bar) int { - barSpacing2 := sbc.GetBarSpacing() >> 1 - bxl := yoffset + barSpacing2 - bxr := bxl + bar.GetHeight() - - normalizedBarComponents := bar.Sections.Normalize(minValue, maxValue) - for index, bv := range normalizedBarComponents { - barWidth := int(math.Ceil(bv.Length * float64(canvasBox.Width()))) - barStart := int(math.Ceil(bv.StartValue * float64(canvasBox.Width()))) - barBox := chart.Box{ - Top: bxl, - Left: util.Math.MinInt(barStart, canvasBox.Right-chart.DefaultStrokeWidth), - Right: util.Math.MinInt(barStart+barWidth, canvasBox.Right-chart.DefaultStrokeWidth), - Bottom: bxr, - } - - chart.Draw.Box(r, barBox, bar.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index))) - } - - return bxr -} - -func (sbc TimelineChart) drawXAxis(r chart.Renderer, canvasBox chart.Box) { - if sbc.XAxis.Show { - axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes()) - axisStyle.WriteToRenderer(r) - - r.MoveTo(canvasBox.Left, canvasBox.Bottom) - r.LineTo(canvasBox.Right, canvasBox.Bottom) - r.Stroke() - - r.MoveTo(canvasBox.Left, canvasBox.Bottom) - r.LineTo(canvasBox.Left, canvasBox.Bottom+chart.DefaultVerticalTickHeight) - r.Stroke() - - cursor := canvasBox.Left - for _, bar := range sbc.Bars { - - barLabelBox := chart.Box{ - Top: canvasBox.Bottom + chart.DefaultXAxisMargin, - Left: cursor, - Right: cursor + bar.GetHeight() + sbc.GetBarSpacing(), - Bottom: sbc.GetHeight(), - } - if len(bar.Name) > 0 { - chart.Draw.TextWithin(r, bar.Name, barLabelBox, axisStyle) - } - axisStyle.WriteToRenderer(r) - r.MoveTo(barLabelBox.Right, canvasBox.Bottom) - r.LineTo(barLabelBox.Right, canvasBox.Bottom+chart.DefaultVerticalTickHeight) - r.Stroke() - cursor += bar.GetHeight() + sbc.GetBarSpacing() - } - } -} - -func (sbc TimelineChart) drawXAxis(r chart.Renderer, canvasBox chart.Box) { - if sbc.XAxis.Show { - axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes()) - axisStyle.WriteToRenderer(r) - r.MoveTo(canvasBox.Right, canvasBox.Top) - r.LineTo(canvasBox.Right, canvasBox.Bottom) - r.Stroke() - - r.MoveTo(canvasBox.Right, canvasBox.Bottom) - r.LineTo(canvasBox.Right+chart.DefaultHorizontalTickWidth, canvasBox.Bottom) - r.Stroke() - - ticks := seq.RangeWithStep(0.0, 1.0, 0.2) - for _, t := range ticks { - axisStyle.GetStrokeOptions().WriteToRenderer(r) - ty := canvasBox.Bottom - int(t*float64(canvasBox.Height())) - r.MoveTo(canvasBox.Right, ty) - r.LineTo(canvasBox.Right+chart.DefaultHorizontalTickWidth, ty) - r.Stroke() - - axisStyle.GetTextOptions().WriteToRenderer(r) - text := fmt.Sprintf("%0.0f%%", t*100) - - tb := r.MeasureText(text) - chart.Draw.Text(r, text, canvasBox.Right+chart.DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle) - } - - } -} - -func (sbc TimelineChart) drawTitle(r chart.Renderer) { - if len(sbc.Title) > 0 && sbc.TitleStyle.Show { - r.SetFont(sbc.TitleStyle.GetFont(sbc.GetFont())) - r.SetFontColor(sbc.TitleStyle.GetFontColor(sbc.GetColorPalette().TextColor())) - titleFontSize := sbc.TitleStyle.GetFontSize(chart.DefaultTitleFontSize) - r.SetFontSize(titleFontSize) - - textBox := r.MeasureText(sbc.Title) - - textWidth := textBox.Width() - textHeight := textBox.Height() - - titleX := (sbc.GetWidth() >> 1) - (textWidth >> 1) - titleY := sbc.TitleStyle.Padding.GetTop(chart.DefaultTitleTop) + textHeight - - r.Text(sbc.Title, titleX, titleY) - } -} - -func (sbc TimelineChart) getCanvasStyle() chart.Style { - return sbc.Canvas.InheritFrom(sbc.styleDefaultsCanvas()) -} - -func (sbc TimelineChart) styleDefaultsCanvas() chart.Style { - return chart.Style{ - FillColor: sbc.GetColorPalette().CanvasColor(), - StrokeColor: sbc.GetColorPalette().CanvasStrokeColor(), - StrokeWidth: chart.DefaultCanvasStrokeWidth, - } -} - -// GetColorPalette returns the color palette for the chart. -func (sbc TimelineChart) GetColorPalette() chart.ColorPalette { - if sbc.ColorPalette != nil { - return sbc.ColorPalette - } - return chart.AlternateColorPalette -} - -func (sbc TimelineChart) getDefaultCanvasBox() chart.Box { - return sbc.Box() -} - -func (sbc TimelineChart) getAdjustedCanvasBox(r chart.Renderer, canvasBox chart.Box) chart.Box { - var totalHeight int - for _, bar := range sbc.Bars { - totalHeight += bar.GetHeight() + sbc.GetBarSpacing() - } - // - //if sbc.YAxis.Show { - // yaxisHeight := chart.DefaultHorizontalTickWidth - // - // axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsAxes()) - // axisStyle.WriteToRenderer(r) - // - // cursor := canvasBox.Bottom - // for _, bar := range sbc.Bars { - // if len(bar.Name) > 0 { - // barLabelBox := chart.Box{ - // Top: cursor + bar.GetHeight() + sbc.GetBarSpacing(), //canvasBox.Bottom + chart.DefaultXAxisMargin, - // Left: canvasBox.Left, //cursor, - // Right: cursor + bar.GetHeight() + sbc.GetBarSpacing(), - // Bottom: sbc.GetHeight(), - // } - // lines := chart.Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle) - // linesBox := chart.Text.MeasureLines(r, lines, axisStyle) - // - // yaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*chart.DefaultXAxisMargin), yaxisHeight) - // } - // } - // return chart.Box{ - // Top: canvasBox.Top, - // Left: canvasBox.Left, - // Right: canvasBox.Left + totalHeight, - // Bottom: sbc.GetHeight() - yaxisHeight, - // } - //} - return chart.Box{ - Top: canvasBox.Top, - Left: canvasBox.Left, - Right: canvasBox.Left + totalHeight, - Bottom: canvasBox.Bottom, - } - -} - -// Box returns the chart bounds as a box. -func (sbc TimelineChart) Box() chart.Box { - dpr := sbc.Background.Padding.GetRight(10) - dpb := sbc.Background.Padding.GetBottom(50) - - return chart.Box{ - Top: sbc.Background.Padding.GetTop(20), - Left: sbc.Background.Padding.GetLeft(20), - Right: sbc.GetWidth() - dpr, - Bottom: sbc.GetHeight() - dpb, - } -} - -func (sbc TimelineChart) styleDefaultsStackedBarValue(index int) chart.Style { - return chart.Style{ - StrokeColor: sbc.GetColorPalette().GetSeriesColor(index), - StrokeWidth: 3.0, - FillColor: sbc.GetColorPalette().GetSeriesColor(index), - } -} - -func (sbc TimelineChart) styleDefaultsTitle() chart.Style { - return sbc.TitleStyle.InheritFrom(chart.Style{ - FontColor: chart.DefaultTextColor, - Font: sbc.GetFont(), - FontSize: sbc.getTitleFontSize(), - TextHorizontalAlign: chart.TextHorizontalAlignCenter, - TextVerticalAlign: chart.TextVerticalAlignTop, - TextWrap: chart.TextWrapWord, - }) -} - -func (sbc TimelineChart) getTitleFontSize() float64 { - effectiveDimension := util.Math.MinInt(sbc.GetWidth(), sbc.GetHeight()) - if effectiveDimension >= 2048 { - return 48 - } else if effectiveDimension >= 1024 { - return 24 - } else if effectiveDimension >= 512 { - return 18 - } else if effectiveDimension >= 256 { - return 12 - } - return 10 -} - -func (sbc TimelineChart) styleDefaultsAxes() chart.Style { - return chart.Style{ - StrokeColor: chart.DefaultAxisColor, - Font: sbc.GetFont(), - FontSize: chart.DefaultAxisFontSize, - FontColor: chart.DefaultAxisColor, - TextHorizontalAlign: chart.TextHorizontalAlignCenter, - TextVerticalAlign: chart.TextVerticalAlignTop, - TextWrap: chart.TextWrapWord, - } -} -func (sbc TimelineChart) styleDefaultsElements() chart.Style { - return chart.Style{ - Font: sbc.GetFont(), - } -}