Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add focus mode #52

Merged
merged 2 commits into from
Sep 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand All @@ -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)
},
Expand Down Expand Up @@ -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)

Expand Down
26 changes: 26 additions & 0 deletions graphviz.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -117,36 +130,46 @@ func graphviz(issues Issues, opts *runOptions) (string, error) {
true,
invisStyle,
))
stats["edges"]++

for i := 1; i < orphansCols; i++ {
panicIfErr(g.AddSubGraph(
"cluster_orphans_without_links",
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(
Expand All @@ -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(
Expand All @@ -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
}
36 changes: 35 additions & 1 deletion issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func (i Issue) NodeTitle() string {
for _, err := range i.Errors {
errors = append(errors, err.Error())
}
errorsText = fmt.Sprintf(`<tr><td bgcolor="red">ERR: %s</td></tr>`, strings.Join(errors, "; "))
errorsText = fmt.Sprintf(`<tr><td bgcolor="red">ERR: %s</td></tr>`, strings.Join(errors, ";<br />ERR: "))
}
return fmt.Sprintf(`<<table><tr><td>%s</td></tr>%s%s%s</table>>`, title, labelsText, assigneeText, errorsText)
}
Expand Down Expand Up @@ -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() {
Expand Down
17 changes: 16 additions & 1 deletion util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}