Merge pull request #475 from jola5/master

Support a vertical graph layout in addition to the existing horizontal
This commit is contained in:
Andrey Smirnov
2017-01-20 23:41:56 +03:00
committed by GitHub
6 changed files with 69 additions and 12 deletions
+1
View File
@@ -24,3 +24,4 @@ List of contributors, in chronological order:
* Geoffrey Thomas (https://github.com/geofft)
* Oliver Sauder (https://github.com/sliverc)
* Harald Sitter (https://github.com/apachelogger)
* Johannes Layher (https://github.com/jola5)
+3 -2
View File
@@ -11,7 +11,7 @@ import (
"os/exec"
)
// GET /api/graph.:ext
// GET /api/graph.:ext?layout=[vertical|horizontal(default)]
func apiGraph(c *gin.Context) {
var (
err error
@@ -19,6 +19,7 @@ func apiGraph(c *gin.Context) {
)
ext := c.Params.ByName("ext")
layout := c.Request.URL.Query().Get("layout")
factory := context.CollectionFactory()
@@ -31,7 +32,7 @@ func apiGraph(c *gin.Context) {
factory.PublishedRepoCollection().RLock()
defer factory.PublishedRepoCollection().RUnlock()
graph, err := deb.BuildGraph(factory)
graph, err := deb.BuildGraph(factory, layout)
if err != nil {
c.JSON(500, err)
return
+5 -1
View File
@@ -21,8 +21,11 @@ func aptlyGraph(cmd *commander.Command, args []string) error {
return commander.ErrCommandError
}
layout := context.Flags().Lookup("layout").Value.String()
fmt.Printf("Generating graph...\n")
graph, err := deb.BuildGraph(context.CollectionFactory())
graph, err := deb.BuildGraph(context.CollectionFactory(), layout)
if err != nil {
return err
}
@@ -108,6 +111,7 @@ Example:
cmd.Flag.String("format", "png", "render graph to specified format (png, svg, pdf, etc.)")
cmd.Flag.String("output", "", "specify output filename, default is to open result in viewer")
cmd.Flag.String("layout", "horizontal", "create a more 'vertical' or a more 'horizontal' graph layout")
return cmd
}
+26 -9
View File
@@ -7,13 +7,28 @@ import (
)
// BuildGraph generates graph contents from aptly object database
func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, error) {
func BuildGraph(collectionFactory *CollectionFactory, layout string) (gographviz.Interface, error) {
var err error
graph := gographviz.NewEscape()
graph.SetDir(true)
graph.SetName("aptly")
var labelStart string
var labelEnd string
switch layout {
case "vertical":
graph.AddAttr("aptly", "rankdir", "LR")
labelStart = ""
labelEnd = ""
case "horizontal":
fallthrough
default:
labelStart = "{"
labelEnd = "}"
}
existingNodes := map[string]bool{}
err = collectionFactory.RemoteRepoCollection().ForEach(func(repo *RemoteRepo) error {
@@ -26,9 +41,9 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkgoldenrod1",
"label": fmt.Sprintf("{Mirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d}",
repo.Name, repo.ArchiveRoot, repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages()),
"label": fmt.Sprintf("%sMirror %s|url: %s|dist: %s|comp: %s|arch: %s|pkgs: %d%s", labelStart, repo.Name, repo.ArchiveRoot,
repo.Distribution, strings.Join(repo.Components, ", "),
strings.Join(repo.Architectures, ", "), repo.NumPackages(), labelEnd),
})
existingNodes[repo.UUID] = true
return nil
@@ -48,8 +63,8 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
"shape": "Mrecord",
"style": "filled",
"fillcolor": "mediumseagreen",
"label": fmt.Sprintf("{Repo %s|comment: %s|pkgs: %d}",
repo.Name, repo.Comment, repo.NumPackages()),
"label": fmt.Sprintf("%sRepo %s|comment: %s|pkgs: %d%s", labelStart,
repo.Name, repo.Comment, repo.NumPackages(), labelEnd),
})
existingNodes[repo.UUID] = true
return nil
@@ -79,7 +94,8 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
"shape": "Mrecord",
"style": "filled",
"fillcolor": "cadetblue1",
"label": fmt.Sprintf("{Snapshot %s|%s|pkgs: %d}", snapshot.Name, description, snapshot.NumPackages()),
"label": fmt.Sprintf("%sSnapshot %s|%s|pkgs: %d%s", labelStart,
snapshot.Name, description, snapshot.NumPackages(), labelEnd),
})
if snapshot.SourceKind == "repo" || snapshot.SourceKind == "local" || snapshot.SourceKind == "snapshot" {
@@ -102,8 +118,9 @@ func BuildGraph(collectionFactory *CollectionFactory) (gographviz.Interface, err
"shape": "Mrecord",
"style": "filled",
"fillcolor": "darkolivegreen1",
"label": fmt.Sprintf("{Published %s/%s|comp: %s|arch: %s}", repo.Prefix, repo.Distribution,
strings.Join(repo.Components(), " "), strings.Join(repo.Architectures, ", ")),
"label": fmt.Sprintf("%sPublished %s/%s|comp: %s|arch: %s%s", labelStart,
repo.Prefix, repo.Distribution, strings.Join(repo.Components(), " "),
strings.Join(repo.Architectures, ", "), labelEnd),
})
for _, uuid := range repo.Sources {
+8
View File
@@ -270,6 +270,14 @@ class BaseTest(object):
if a != b:
self.verify_match(a, b, match_prepare=pprint.pformat)
def check_ge(self, a, b):
if not a >= b:
raise Exception("%s is not greater or equal to %s" % (a, b))
def check_gt(self, a, b):
if not a > b:
raise Exception("%s is not greater to %s" % (a, b))
def check_in(self, item, l):
if not item in l:
raise Exception("item %r not in %r", item, l)
+26
View File
@@ -1,4 +1,5 @@
from api_lib import APITest
import xml.etree.ElementTree as ET
class GraphAPITest(APITest):
@@ -19,3 +20,28 @@ class GraphAPITest(APITest):
resp = self.get("/api/graph.dot")
self.check_equal(resp.headers["Content-Type"], "text/plain; charset=utf-8")
self.check_equal(resp.content[:13], 'digraph aptly')
# basic test of layout:
# horizontal should be wider than vertical
# vertical should be higher than horizontal
# for this to work we need at couple of repos
tempRepos = [self.random_name() for r in range(3)]
for repo in tempRepos:
self.check_equal(self.post("/api/repos", json={"Name": repo, "Comment": "graph test repo"}).status_code, 201)
horizontal = self.get("/api/graph.svg?layout=horizontal").content
vertical = self.get("/api/graph.svg?layout=vertical").content
horizontalWidth = int(ET.fromstring(horizontal).get('width').replace("pt",""))
horizontalHeight = int(ET.fromstring(horizontal).get('height').replace("pt",""))
verticalWidth = int(ET.fromstring(vertical).get('width').replace("pt",""))
verticalHeight = int(ET.fromstring(vertical).get('height').replace("pt",""))
self.check_gt(horizontalWidth, verticalWidth)
self.check_gt(verticalHeight, horizontalHeight)
# make sure our default layout is horizontal
self.check_equal(horizontal, self.get("/api/graph.svg").content)
# remove the repos again
for repo in tempRepos:
self.check_equal(self.delete("/api/repos/" + repo, params={"force": "1"}).status_code, 200)