jobsidian/main.go
Evgenii Stratonikov 3fdaad8e86
initial commit
Signed-off-by: Evgenii Stratonikov <stratonikov@runbox.com>
2024-10-28 14:02:04 +03:00

169 lines
3.4 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"github.com/ankitpokhrel/jira-cli/pkg/jira"
"github.com/davecgh/go-spew/spew"
)
var (
server = flag.String("server", "", "server endpoint")
token = flag.String("token", "", "path to token")
debug = flag.Bool("debug", false, "debug")
epic = flag.String("epic", "", "epic to build graph of")
out = flag.String("out", "", "")
)
func main() {
flag.Parse()
c := newClient(*server, *token)
if *out != "" {
ret, err := c.Me()
failOnError(err)
fmt.Printf("Hello, %s (%s)\n", ret.Login, ret.Email)
}
res, err := c.Structure()
spew.Config.DisableMethods = true
spew.Dump(res, err)
return
issues := epicIssues(c, *epic)
const height = 120
const width = 300
var canvas Canvas
sorted := topologicalSort(issues)
column := 0
row := 0
columns := make(map[string]int)
for _, issue := range sorted {
key := issue.Key
fmt.Fprintln(os.Stderr, "%s %s %s", issue.Key, issue.Fields.Watches)
for _, link := range issue.Fields.IssueLinks {
switch link.LinkType.Name {
case "Depends":
if link.OutwardIssue != nil {
from := link.OutwardIssue.Key
if _, ok := issues[from]; !ok {
continue
}
fromSide := "right"
to := issue.Key
toSide := "left"
col, ok := columns[from]
assert(ok)
if col == column {
column++
row = 0
}
edge := Edge{
ID: fmt.Sprintf("%s-%s", from, to),
FromNode: from,
FromSide: fromSide,
ToNode: to,
ToSide: toSide,
}
canvas.Edges = append(canvas.Edges, edge)
}
}
}
var node Node
node.ID = key
node.Type = Text
node.X = column * (width + 100)
node.Y = row * (height + 40)
node.Width = 300
node.Height = height
node.Text = fmt.Sprintf("[%s](%s/browse/%s): %s", issue.Key, *server, issue.Key, issue.Fields.Summary)
switch issue.Fields.Priority.Name {
case "Highest":
node.Color = Red
case "High":
node.Color = Orange
case "Medium":
// Medium priority is the most common, ignore coloring.
// node.Color = Yellow
case "Low":
node.Color = Cyan
}
canvas.Nodes = append(canvas.Nodes, node)
columns[key] = column
row++
}
data, err := json.Marshal(canvas)
failOnError(err)
fmt.Println(string(data))
}
func topologicalSort(issues map[string]*jira.Issue) []*jira.Issue {
type compactIssue struct {
key string
dependsOn []string
}
var list []compactIssue
for _, issue := range issues {
var ci compactIssue
ci.key = issue.Key
for _, link := range issue.Fields.IssueLinks {
switch link.LinkType.Name {
case "Depends":
if link.OutwardIssue != nil {
_, inEpic := issues[link.OutwardIssue.Key]
if inEpic {
ci.dependsOn = append(ci.dependsOn, link.OutwardIssue.Key)
}
}
}
}
list = append(list, ci)
}
// Kahn's algorithm.
set := make(map[string]int)
for i, ci := range list {
if len(ci.dependsOn) == 0 {
set[ci.key] = i
}
}
var sorted []*jira.Issue
for len(set) != 0 {
var key string
var index int
for key, index = range set {
break
}
delete(set, key)
sorted = append(sorted, issues[key])
n := list[index]
for _, m := range list {
for i, inward := range m.dependsOn {
if inward == n.key {
m.dependsOn = append(m.dependsOn[:i], m.dependsOn[i+1:]...)
if len(m.dependsOn) == 0 {
set[m.key] = i
}
break
}
}
}
}
return sorted
}