From 7c5a284717cd18afb46bf61649641147b6f65088 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 19 Sep 2018 16:30:46 +0200 Subject: [PATCH 1/2] feat: add focus mode --- cmd_run.go | 26 ++++++++++++++------------ issue.go | 34 ++++++++++++++++++++++++++++++++++ util.go | 17 ++++++++++++++++- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/cmd_run.go b/cmd_run.go index 88a814af8..d1a7efb06 100644 --- a/cmd_run.go +++ b/cmd_run.go @@ -15,18 +15,20 @@ import ( type runOptions struct { // pull - PullOpts pullOptions - NoPull bool + PullOpts pullOptions + NoPull bool + ReposToFetch []string // db DBOpts dbOptions // run - ShowClosed bool `mapstructure:"show-closed"` - ShowOrphans bool - EpicLabel string - Destination string - DebugGraph bool + ShowClosed bool `mapstructure:"show-closed"` + ShowOrphans bool + AdditionalPulls []string + EpicLabel string + Destination string + DebugGraph bool Targets []string //Preview bool @@ -44,6 +46,7 @@ func runSetupFlags(flags *pflag.FlagSet, opts *runOptions) { flags.BoolVarP(&opts.ShowOrphans, "show-orphans", "", false, "show issues not linked to an epic") flags.StringVarP(&opts.EpicLabel, "epic-label", "", "epic", "label used for epics (empty means issues with dependencies but without dependants)") flags.StringVarP(&opts.Destination, "destination", "", "-", "destination ('-' for stdout)") + flags.StringSliceVarP(&opts.AdditionalPulls, "additional-pull", "", []string{}, "additional pull that won't necessarily be displayed on the graph") //flags.BoolVarP(&opts.Preview, "preview", "p", false, "preview result") viper.BindPFlags(flags) } @@ -63,7 +66,7 @@ func newRunCommand() *cobra.Command { return err } opts.PullOpts.DBOpts = opts.DBOpts - opts.PullOpts.Targets = args + opts.PullOpts.Targets = append(args, opts.AdditionalPulls...) opts.Targets = args return run(opts) }, @@ -91,14 +94,13 @@ func run(opts *runOptions) error { return errors.Wrap(err, "failed to prepare issues") } - issues.processEpicLinks() if !opts.ShowClosed { issues.HideClosed() } - if !opts.ShowOrphans && issues.HasNonOrphans() { - issues.HideOrphans() + issues.filterByTargets(opts.Targets) + if opts.ShowOrphans { + logger().Warn("--show-orphans is deprecated and will be removed") } - issues.processEpicLinks() out, err := graphviz(issues, opts) diff --git a/issue.go b/issue.go index 75c18367a..fffcb6e8e 100644 --- a/issue.go +++ b/issue.go @@ -471,6 +471,40 @@ func (issues Issues) processEpicLinks() { } } +func (issues Issues) filterByTargets(targets []string) { + for _, issue := range issues { + if issue.Hidden { + continue + } + issue.Hidden = !issue.MatchesWithATarget(targets) + } +} + +func (i Issue) MatchesWithATarget(targets []string) bool { + issueParts := strings.Split(strings.TrimRight(i.URL, "/"), "/") + for _, target := range targets { + fullTarget := i.GetRelativeIssueURL(target) + targetParts := strings.Split(strings.TrimRight(fullTarget, "/"), "/") + if len(issueParts) == len(targetParts) { + if i.URL == fullTarget { + return true + } + } else { + if i.URL[:len(fullTarget)] == fullTarget { + return true + } + } + } + + for _, parent := range i.Blocks { + if parent.MatchesWithATarget(targets) { + return true + } + } + + return false +} + func (issues Issues) HideClosed() { for _, issue := range issues { if issue.IsClosed() { diff --git a/util.go b/util.go index ccb766350..a706fbecb 100644 --- a/util.go +++ b/util.go @@ -44,11 +44,26 @@ func getReposFromTargets(targets []string) []string { logger().Fatal("filesystem target are not yet supported") } repo := strings.Split(target, "/issues")[0] + repo = strings.Split(target, "#")[0] reposMap[repo] = true } repos := []string{} for repo := range reposMap { repos = append(repos, repo) } - return repos + return uniqueStrings(repos) +} + +func uniqueStrings(input []string) []string { + u := make([]string, 0, len(input)) + m := make(map[string]bool) + + for _, val := range input { + if _, ok := m[val]; !ok { + m[val] = true + u = append(u, val) + } + } + + return u } From f249a6b4fbb15f12a46cf0d0f8255edc1310d85b Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 19 Sep 2018 19:41:24 +0200 Subject: [PATCH 2/2] feat: add graph stats --- graphviz.go | 26 ++++++++++++++++++++++++++ issue.go | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/graphviz.go b/graphviz.go index 7f552a5c5..bf4ea14af 100644 --- a/graphviz.go +++ b/graphviz.go @@ -6,10 +6,17 @@ import ( "sort" "github.com/awalterschulze/gographviz" + "go.uber.org/zap" ) func graphviz(issues Issues, opts *runOptions) (string, error) { var ( + stats = map[string]int{ + "nodes": 0, + "edges": 0, + "hidden": 0, + "subgraphs": 0, + } invisStyle = map[string]string{"style": "invis", "label": escape("")} weightMap = map[int]bool{} weights = []int{} @@ -19,6 +26,7 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { } for _, issue := range issues { if issue.Hidden { + stats["hidden"]++ continue } weightMap[issue.Weight()] = true @@ -83,11 +91,13 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { } panicIfErr(issue.AddNodeToGraph(g, parent)) + stats["nodes"]++ } // issue relationships for _, issue := range issues { panicIfErr(issue.AddEdgesToGraph(g)) + stats["edges"]++ } // orphans cluster and placeholder @@ -97,18 +107,21 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { "cluster_orphans_without_links", map[string]string{"label": escape("orphans without links"), "style": "dashed"}, )) + stats["subgraphs"]++ panicIfErr(g.AddSubGraph( "cluster_orphans_without_links", "cluster_orphans_without_links_0", invisStyle, )) + stats["subgraphs"]++ for i := 0; i < orphansCols; i++ { panicIfErr(g.AddNode( fmt.Sprintf("cluster_orphans_without_links_%d", i), fmt.Sprintf("placeholder_orphans_without_links_%d", i), invisStyle, )) + stats["nodes"]++ } panicIfErr(g.AddEdge( @@ -117,6 +130,7 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { true, invisStyle, )) + stats["edges"]++ for i := 1; i < orphansCols; i++ { panicIfErr(g.AddSubGraph( @@ -124,29 +138,38 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { fmt.Sprintf("cluster_orphans_without_links_%d", i), invisStyle, )) + stats["subgraphs"]++ panicIfErr(g.AddEdge( fmt.Sprintf("placeholder_orphans_without_links_%d", i-1), fmt.Sprintf("placeholder_orphans_without_links_%d", i), true, invisStyle, )) + stats["edges"]++ } } if hasOrphansWithLinks { panicIfErr(g.AddSubGraph("G", "cluster_orphans_with_links", map[string]string{"label": escape("orphans with links"), "style": "dashed"})) + stats["subgraphs"]++ + panicIfErr(g.AddNode("cluster_orphans_with_links", "placeholder_orphans_with_links", invisStyle)) + stats["nodes"]++ + panicIfErr(g.AddEdge( "placeholder_orphans_with_links", fmt.Sprintf("placeholder_%d", weights[0]), true, invisStyle, )) + stats["edges"]++ } // set weights clusters and placeholders for _, weight := range weights { clusterName := fmt.Sprintf("anon%d", weight) panicIfErr(g.AddSubGraph("G", clusterName, map[string]string{"rank": "same"})) + stats["subgraphs"]++ + //clusterName := fmt.Sprintf("cluster_w%d", weight) //panicIfErr(g.AddSubGraph("G", clusterName, map[string]string{"label": fmt.Sprintf("w%d", weight)})) panicIfErr(g.AddNode( @@ -157,6 +180,7 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { "label": fmt.Sprintf(`"weight=%d"`, weight), }, )) + stats["nodes"]++ } for i := 0; i < len(weights)-1; i++ { panicIfErr(g.AddEdge( @@ -165,7 +189,9 @@ func graphviz(issues Issues, opts *runOptions) (string, error) { true, invisStyle, )) + stats["edges"]++ } + logger().Debug("graph stats", zap.Any("stats", stats)) return g.String(), nil } diff --git a/issue.go b/issue.go index fffcb6e8e..d18acc98a 100644 --- a/issue.go +++ b/issue.go @@ -242,7 +242,7 @@ func (i Issue) NodeTitle() string { for _, err := range i.Errors { errors = append(errors, err.Error()) } - errorsText = fmt.Sprintf(`ERR: %s`, strings.Join(errors, "; ")) + errorsText = fmt.Sprintf(`ERR: %s`, strings.Join(errors, ";
ERR: ")) } return fmt.Sprintf(`<%s%s%s
%s
>`, title, labelsText, assigneeText, errorsText) }