shithub: jirafs

Download patch

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})
 		}
 	}