ref: dea4abf84413766eeb039b26c3c12c64d6d4e147
parent: 6b44a1578f84079dca6b0896926ca2134374e1f5
author: Kenny Levinsen <kl@codesealer.com>
date: Tue Jun 7 13:01:10 EDT 2016
More stuff
--- a/jira.go
+++ b/jira.go
@@ -1,6 +1,7 @@
package main
import (
+ "encoding/json"
"errors"
"fmt"
"log"
@@ -7,6 +8,7 @@
"strconv"
"strings"
"sync"
+ "time"
"github.com/andygrunwald/go-jira"
"github.com/joushou/qp"
@@ -96,7 +98,7 @@
}
func (iw *IssueView) normalFiles() (files, dirs []string) {
- files = []string{"assignee", "creator", "ctl", "description", "issuetype", "key", "reporter", "status", "summary", "labels", "transitions"}
+ files = []string{"assignee", "creator", "ctl", "description", "issuetype", "key", "reporter", "status", "summary", "labels", "transitions", "priority", "resolution", "raw", "progress"}
dirs = []string{"comments"}
return
}
@@ -231,6 +233,21 @@
if issue.Fields != nil && issue.Fields.Status != nil {
sf.SetContent([]byte(issue.Fields.Status.Name + "\n"))
}
+ case "priority":
+ if issue.Fields != nil && issue.Fields.Priority != nil {
+ sf.SetContent([]byte(issue.Fields.Priority.Name + "\n"))
+ }
+ case "resolution":
+ if issue.Fields != nil && issue.Fields.Resolution != nil {
+ sf.SetContent([]byte(issue.Fields.Resolution.Name + "\n"))
+ }
+ case "progress":
+ if issue.Fields != nil && issue.Fields.Progress != nil {
+ p := time.Duration(issue.Fields.Progress.Progress) * time.Second
+ t := time.Duration(issue.Fields.Progress.Total) * time.Second
+ percent := int((1 - float64(issue.Fields.Progress.Total-issue.Fields.Progress.Progress)/float64(issue.Fields.Progress.Total)) * 100)
+ sf.SetContent([]byte(fmt.Sprintf("Progress: %v, Total: %v, Percent: %d%%\n", p, t, percent)))
+ }
case "key":
sf.SetContent([]byte(issue.Key + "\n"))
case "labels":
@@ -260,6 +277,12 @@
"jira",
jc,
&CommentView{project: iw.project, issueNo: iw.issueNo})
+ case "raw":
+ b, err := json.MarshalIndent(issue, "", " ")
+ if err != nil {
+ return nil, err
+ }
+ sf.SetContent(b)
case "ctl":
cmds := map[string]func([]string) error{
"delete": func(args []string) error {
@@ -272,7 +295,7 @@
onClose := func() error {
switch file {
- case "key":
+ case "key", "raw":
return nil
case "transitions":
sf.Lock()
@@ -308,7 +331,7 @@
return err
}
- p, err := wg.Path(issue.Fields.Status.Name, str, 10000)
+ p, err := wg.Path(issue.Fields.Status.Name, str, 500)
if err != nil {
log.Printf("Could not find path: %v", err)
log.Printf("Workflow: \n%s\n", wg.Dump())
--- a/utils.go
+++ b/utils.go
@@ -3,8 +3,6 @@
import (
"errors"
"fmt"
- "log"
- "net/http/httputil"
"strings"
"time"
@@ -198,13 +196,13 @@
case "labels":
var labels []string
if val != "" && val != "\n" {
- labels := strings.Split(val, "\n")
+ labels = strings.Split(val, "\n")
if labels[len(labels)-1] == "" {
labels = labels[:len(labels)-1]
}
}
fields[field] = labels
- case "issuetype", "assignee", "reporter", "creator":
+ case "issuetype", "assignee", "reporter", "creator", "priority", "resolution":
fields[field] = map[string]interface{}{
"name": value,
}
@@ -214,10 +212,6 @@
req, err := jc.NewRequest(method, cmd, post)
if err != nil {
return fmt.Errorf("could not query JIRA: %v", err)
- }
-
- if b, err := httputil.DumpRequestOut(req, true); err == nil {
- log.Printf("SetFieldInIssue body: \n%s\n", b)
}
if _, err = jc.Do(req, nil); err != nil {
--- a/workflow.go
+++ b/workflow.go
@@ -4,6 +4,7 @@
"errors"
"fmt"
"net/url"
+ "strings"
"github.com/andygrunwald/go-jira"
)
@@ -140,6 +141,7 @@
}
type WorkflowGraph struct {
+ // verteces is a map of lower-cased status named to their status struct.
verteces map[string]*Status
}
@@ -158,7 +160,7 @@
ID: s.ID,
}
- wg.verteces[s.Name] = l
+ wg.verteces[strings.ToLower(s.Name)] = l
local[s.TransitionID] = l
}
@@ -184,19 +186,19 @@
wg.verteces = make(map[string]*Status)
}
for _, elem := range wr.Sources {
- name := elem.FromStatus.Name
+ name := strings.ToLower(elem.FromStatus.Name)
fromStatus, exists := wg.verteces[name]
if !exists {
fromStatus = elem.FromStatus.Status()
- wg.verteces[fromStatus.Name] = fromStatus
+ wg.verteces[name] = fromStatus
}
for _, target := range elem.Targets {
- targetName := target.ToStatus.Name
+ targetName := strings.ToLower(target.ToStatus.Name)
targetStatus, exists := wg.verteces[targetName]
if !exists {
targetStatus = target.ToStatus.Status()
- wg.verteces[targetStatus.Name] = targetStatus
+ wg.verteces[name] = targetStatus
}
targetEdge := StatusEdge{
Name: target.TransitionName,
@@ -226,14 +228,21 @@
edge StatusEdge
}
+// Path finds the shortest path in the workflow graph from A to B, searching at
+// most limit verteces. A negative limit results in path executing without a
+// limit. Cycles are detected and terminated, so the limit is just to avoid high
+// searching times in *very* large graphs. A and B are case insensitive for
+// convenience.
func (wg *WorkflowGraph) Path(A, B string, limit int) ([]string, error) {
- statusA := wg.verteces[A]
- statusB := wg.verteces[B]
+ statusA := wg.verteces[strings.ToLower(A)]
+ statusB := wg.verteces[strings.ToLower(B)]
if statusA == nil || statusB == nil {
return nil, errors.New("no such status")
}
+ visited := make(map[string]bool)
+
var search []path
for _, edge := range statusA.Edges {
search = append(search, path{edge: edge})
@@ -253,7 +262,7 @@
start := &p
for {
- s = append(s, start.edge.Name)
+ s = append([]string{start.edge.Name}, s...)
if start.from == nil {
break
}
@@ -260,17 +269,17 @@
start = start.from
}
-
- for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
- s[i], s[j] = s[j], s[i]
- }
-
return s, nil
}
+ if visited[p.edge.Status.ID] {
+ // We have already walked all edges of this vertice.
+ continue
+ }
+ visited[p.edge.Status.ID] = true
+
// Add the edges to the search.
for _, edge := range p.edge.Status.Edges {
- // log.Printf("%s -> %s", p.edge.Status.Name, edge.Name)
search = append(search, path{from: &p, edge: edge})
}
}