Skip to content

Commit

Permalink
Merge pull request #3 from philippdrebes/dep-graph
Browse files Browse the repository at this point in the history
Issue dependency graph
  • Loading branch information
philippdrebes authored Jun 13, 2018
2 parents d8f74d1 + ddeeaf6 commit 6a41b04
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Use of this source code is governed by a MIT-style license that can
# be found in the LICENSE file.

VERSION = 0.2.0
VERSION = 0.3.0
PACKAGE = github.com/philippdrebes/goji/cmd/goji

run:
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

goji is the simple and fast way to access [Atlassian Jira](https://atlassian.com/software/jira) from your command line.

## Requirements
- [graphviz](http://www.graphviz.org/download/)

## Build

```shell session
Expand All @@ -14,4 +17,10 @@ make build

```shell session
make install
```
```

## Create Graph

```shell session
dot -Tpng > graph.png
```
57 changes: 55 additions & 2 deletions cmd/goji/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
"runtime"
"github.com/atotto/clipboard"
"github.com/philippdrebes/goji"
"io/ioutil"
"path"
"time"
"github.com/skratchdot/open-golang/open"
"path/filepath"
)

var clear map[string]func() //create a map for storing clear funcs
Expand Down Expand Up @@ -63,7 +68,6 @@ func main() {
return
}

goji.GetConfig()
client, err := login()

if client == nil || err != nil {
Expand All @@ -75,6 +79,7 @@ func main() {

var actions []Action
actions = append(actions, Action{"assignedTasks", "Display assigned tasks", displayAssignedTasks})
actions = append(actions, Action{"linkedIssueGraph", "Display graph of linked issues", createLinkedIssueGraph})
actions = append(actions, Action{"quit", "Quit", nil})

for {
Expand Down Expand Up @@ -143,6 +148,54 @@ func displayAssignedTasks(client *goji.Client) {
}
}

func createLinkedIssueGraph(client *goji.Client) {
var issueKey string
fmt.Println("Issue:")
fmt.Scanf("%s", &issueKey)

fmt.Printf("Loading issue %s\n", issueKey)
issue, _, _ := client.JiraClient.Issue.Get(issueKey, nil)

fmt.Println("Generating dependency graph in dot language")
graph := goji.BuildGraph(client.JiraClient, issue)

dotFile, err := ioutil.TempFile(os.TempDir(), "goji-deps")
if err != nil {
fmt.Printf("\nError while opening temp dotFile.\n%v\n", err)
return
}
defer os.Remove(dotFile.Name())

err = ioutil.WriteFile(dotFile.Name(), []byte(graph.String()), 0755)

fmt.Println("Generating png from dot language")
png, err := exec.Command("dot", "-Tpng", dotFile.Name()).Output()
if err != nil {
fmt.Printf("\nError while creating graph.\n%v\n", err)
return
}

ex, err := os.Executable()
if err != nil {
panic(err)
}
exPath := filepath.Dir(ex)

now := time.Now()
pngFile := path.Join(exPath, fmt.Sprintf("%s_%d-%02d-%02d.png", issue.Key, now.Year(), now.Month(), now.Day()))

err = ioutil.WriteFile(pngFile, png, 0755)

if err != nil {
fmt.Printf("\nError while saving graph.\n%v\n", err)
return
}

fmt.Println("Done")
open.Start(pngFile)
return
}

func login() (*goji.Client, error) {
url, username, password := getCredentials()
client, err := goji.NewClient(url, username, password)
Expand Down Expand Up @@ -191,7 +244,7 @@ func getCredentials() (string, string, string) {
fmt.Print("\nSave as default? [Y/n]: ")
save, _ := r.ReadString('\n')

if strings.ToLower(strings.TrimSpace(save)) != "n" {
if strings.ToLower(strings.TrimSpace(save)) != "n" {
config.Url = url
config.Username = username
goji.SaveConfig(config)
Expand Down
90 changes: 90 additions & 0 deletions graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package goji

import (
"github.com/andygrunwald/go-jira"
"github.com/awalterschulze/gographviz"
"fmt"
"strings"
)

const (
inward int = 0
outward int = 1
)

func wrapInQuotes(value string) string {
return "\"" + value + "\""
}

func getStatusColor(issue *jira.Issue) string {
status := strings.ToUpper(issue.Fields.Status.Name)
if status == "IN PROGRESS" {
return "yellow"
} else if status == "DONE" {
return "green"
}
return "white"
}

func addNode(graph *gographviz.Graph, issue *jira.Issue) {
if graph.IsNode(issue.Key) == true {
return
}

attrs := make(map[string]string)
attrs[string(gographviz.Shape)] = "box"
attrs[string(gographviz.FillColor)] = getStatusColor(issue)
attrs[string(gographviz.Style)] = "filled"
attrs[string(gographviz.Label)] = wrapInQuotes(fmt.Sprintf("%s\n%s", issue.Key, issue.Fields.Summary))

graph.AddNode("G", wrapInQuotes(issue.Key), attrs)
}

func walk(client *jira.Client, issue *jira.Issue, graph *gographviz.Graph) *gographviz.Graph {
if graph == nil {
graph = gographviz.NewGraph()
if err := graph.SetDir(true); err != nil {
panic(err)
}
graph.Attrs.Add(string(gographviz.DPI), "300")
}

addNode(graph, issue)

for _, li := range issue.Fields.IssueLinks {
var direction int
var linkedIssue *jira.Issue
if li.InwardIssue != nil {
linkedIssue = li.InwardIssue
direction = inward
} else if li.OutwardIssue != nil {
linkedIssue = li.OutwardIssue
direction = outward
}

// create node
linkedIssue, _, err := client.Issue.Get(linkedIssue.ID, nil)
if err != nil {
panic(err)
}

if graph.IsNode(wrapInQuotes(linkedIssue.Key)) == false {
walk(client, linkedIssue, graph)
}

// create edge
attrs := make(map[string]string)
if direction == inward {
attrs[string(gographviz.Label)] = wrapInQuotes(li.Type.Inward)
} else if direction == outward {
attrs[string(gographviz.Label)] = wrapInQuotes(li.Type.Outward)
}
graph.AddEdge(wrapInQuotes(issue.Key), wrapInQuotes(linkedIssue.Key), true, attrs)
}

return graph
}

func BuildGraph(client *jira.Client, issue *jira.Issue) *gographviz.Graph {
return walk(client, issue, nil)
}

0 comments on commit 6a41b04

Please sign in to comment.