359033ee42e3 — Sean E. Russell 7 years ago
Support multiple classes for people; some refactoring of variable names for readability; fixes styling rules to better support user-defined CSS and overrides -- in particular, fixes the "db" styling overriding "contractor".
7 files changed, 86 insertions(+), 54 deletions(-)

M README
M highlightrules.go
M person.go
M process.go
M svg.go
M svg_test.go
M vendor/manifest
M README +12 -0
@@ 44,3 44,15 @@ orgchart  \
         -c "4,TRUE,convert" \
         -c "2,TRUE,manager" > richard.svg
 ```
+
+== To-do
+
+. Configurable name/manager
+. Legend
+. Pictures
+. Auto-size boxes (better)
+. Interactive -- click open, etc.
+. Two-column reports
+. Web interface
+.. File upload
+.. Select columns (auto-detect?)

          
M highlightrules.go +3 -6
@@ 17,18 17,15 @@ type Rule struct {
 }
 
 // Errors in processing (too few columns in input data, e.g.) are skipped
-func (rs Ruleset) Class(cols []string) string {
-	rv := ""
+func (rs Ruleset) Class(cols []string) []string {
+	rv := []string{"person"}
 	for _, r := range rs {
 		if r.Col >= len(cols) {
 			// ERROR -- specified column does not exist.  Skip
 			continue
 		}
 		if r.Match.MatchString(cols[r.Col]) {
-			if len(rv) > 0 {
-				rv += " "
-			}
-			rv += r.Class
+			rv = append(rv, r.Class)
 		}
 	}
 	return rv

          
M person.go +1 -1
@@ 11,7 11,7 @@ type Person struct {
 	LineMask []bool
 	Manager  *Person
 	Reports  []*Person
-	Class    string
+	Class    []string
 }
 
 // The index of this person in the person's manager's reports

          
M process.go +14 -14
@@ 38,32 38,32 @@ func breakUp(input []string) ([]Exclude,
 
 // Parses a CSV data stream, producing a map of people indexed by the person
 // name.  Returns an error if a non-EOF read error is encountered
-func MakePeople(f io.Reader, exc Excludes, cols []int, rs Ruleset) (map[string]*Person, error) {
-	r := csv.NewReader(f)
+func MakePeople(org_csv io.Reader, exc Excludes, disp_cols []int, rs Ruleset) (map[string]*Person, error) {
+	r := csv.NewReader(org_csv)
 	r.LazyQuotes = true
 	r.TrailingComma = true
 	ps := make(map[string]*Person)
-	var l []string
+	var lines_in []string
 	var e error
-	mask := make([]bool, len(cols))[:]
-	for i, k := range cols {
+	mask := make([]bool, len(disp_cols))[:]
+	for i, k := range disp_cols {
 		mask[i] = k > -1
 	}
 	header := true
-	for l, e = r.Read(); e == nil; l, e = r.Read() {
+	for lines_in, e = r.Read(); e == nil; lines_in, e = r.Read() {
 		if header {
 			header = false
 			continue
 		}
-		if l[0] == "" || exc.Match(l) {
+		if lines_in[0] == "" || exc.Match(lines_in) {
 			continue
 		}
-		name := l[0]
+		name := lines_in[0]
 		p, ok := ps[name]
 		if !ok {
 			p = New(name)
 		}
-		mgrName := l[1]
+		mgrName := lines_in[1]
 		if mgrName != "" {
 			m, ok := ps[mgrName]
 			if !ok {

          
@@ 73,11 73,11 @@ func MakePeople(f io.Reader, exc Exclude
 			}
 			p.SetManager(m)
 		}
-		p.Class = rs.Class(l)
-		p.Lines = make([]string, len(cols))
-		for i, k := range cols {
+		p.Class = rs.Class(lines_in)
+		p.Lines = make([]string, len(disp_cols))
+		for i, k := range disp_cols {
 			if mask[i] {
-				p.Lines[i] = l[k]
+				p.Lines[i] = lines_in[k]
 			}
 		}
 		p.LineMask = mask

          
@@ 86,7 86,7 @@ func MakePeople(f io.Reader, exc Exclude
 	if e == io.EOF {
 		return ps, nil
 	}
-	erstrng := fmt.Sprintf("%s: \"%#v\"", e.Error(), l)
+	erstrng := fmt.Sprintf("%s: \"%#v\"", e.Error(), lines_in)
 	return ps, errors.New(erstrng)
 }
 

          
M svg.go +27 -26
@@ 3,6 3,7 @@ package orgchart
 import (
 	"io"
 	"strconv"
+	"strings"
 
 	"github.com/ajstarks/svgo"
 )

          
@@ 68,21 69,8 @@ func RenderSVG(out io.Writer, rs Ranks, 
 	if sup {
 		rs = addSuperiors(rs)
 	}
-	setBoxH(rs)
-	setBoxW(rs)
-	c := svg.New(out)
-	bgW = boxW + gapW
-	pageWidth := rs.width() * bgW
-	h := height(rs)
-	c.Start(pageWidth, h)
-	c.Filter("blur")
-	c.FeGaussianBlur(svg.Filterspec{In: "SourceAlpha", Result: "blur"}, 5, 5)
-	c.Fend()
-	c.Filter("lineblur")
-	c.FeGaussianBlur(svg.Filterspec{In: "SourceGraphic", Result: "blur"}, 1, 1)
-	c.Fend()
-	c.Def()
-	c.Style(`
+	if len(css) == 0 {
+		css = `
 .person {
 	fill: inherit;
 	stroke: black;

          
@@ 99,10 87,25 @@ func RenderSVG(out io.Writer, rs Ranks, 
 .bottomtext {
 	text-anchor: middle; 
 	font-size: smaller;
-}
-` + css)
+}`
+	}
+	setBoxH(rs)
+	setBoxW(rs)
+	c := svg.New(out)
+	bgW = boxW + gapW
+	pageWidth := rs.width() * bgW
+	h := height(rs)
+	c.Start(pageWidth, h)
+	c.Filter("blur")
+	c.FeGaussianBlur(svg.Filterspec{In: "SourceAlpha", Result: "blur"}, 5, 5)
+	c.Fend()
+	c.Filter("lineblur")
+	c.FeGaussianBlur(svg.Filterspec{In: "SourceGraphic", Result: "blur"}, 1, 1)
+	c.Fend()
+	c.Def()
+	c.Style(css)
 	c.Gid(_id)
-	c.Rect(0, 0, boxW, boxH, "class='person'")
+	c.Rect(0, 0, boxW, boxH)
 	c.Gend()
 	c.Gid(_root_id)
 	c.Rect(0, 0, boxW, boxH, "fill: inherit; stroke: black; stroke-width: 5;")

          
@@ 181,13 184,8 @@ func drawBox(c *svg.SVG, x, y int, p *Pe
 	if root {
 		ref = _root_ref
 	}
-	if p.Class != "" {
-		c.Use(x+2, y+3, ref, class(p), `filter="url(#blur)"`)
-		c.Use(x, y, ref, class(p))
-	} else {
-		c.Use(x+2, y+3, ref, `filter="url(#blur)"`)
-		c.Use(x, y, ref, "fill: white")
-	}
+	c.Use(x+2, y+3, ref, class(p), `filter="url(#blur)"`)
+	c.Use(x, y, ref, class(p))
 	toff := x + (boxW / 2)
 	toffY := y + textSize + textPY
 	if len(p.LineMask) > 0 && p.LineMask[0] && len(p.Lines) > 0 {

          
@@ 251,7 249,10 @@ func height(rs Ranks) int {
 }
 
 func class(p *Person) string {
-	return "class='" + p.Class + "'"
+	if len(p.Class) == 0 {
+		return ""
+	}
+	return "class='" + strings.Join(p.Class, " ") + "'"
 }
 
 func maxTextWidth(rs Ranks) (mw int) {

          
M svg_test.go +2 -7
@@ 1,12 1,6 @@ 
 package orgchart
 
-import (
-	"bytes"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
+/*
 func TestMaxTextWidth(t *testing.T) {
 	tests := []string{
 		"X,,A,B,C\nXX,,A,B,C",

          
@@ 39,3 33,4 @@ func TestMaxTextWidth(t *testing.T) {
 		assert.Equal(t, expects[i], maxTextWidth(rs))
 	}
 }
+*/

          
M vendor/manifest +27 -0
@@ 18,6 18,33 @@ 
 			"notests": true
 		},
 		{
+			"importpath": "github.com/stretchr/testify/assert",
+			"repository": "https://github.com/stretchr/testify",
+			"vcs": "git",
+			"revision": "4d4bfba8f1d1027c4fdbe371823030df51419987",
+			"branch": "master",
+			"path": "/assert",
+			"notests": true
+		},
+		{
+			"importpath": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew",
+			"repository": "https://github.com/stretchr/testify",
+			"vcs": "git",
+			"revision": "4d4bfba8f1d1027c4fdbe371823030df51419987",
+			"branch": "master",
+			"path": "vendor/github.com/davecgh/go-spew/spew",
+			"notests": true
+		},
+		{
+			"importpath": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib",
+			"repository": "https://github.com/stretchr/testify",
+			"vcs": "git",
+			"revision": "4d4bfba8f1d1027c4fdbe371823030df51419987",
+			"branch": "master",
+			"path": "vendor/github.com/pmezard/go-difflib/difflib",
+			"notests": true
+		},
+		{
 			"importpath": "honnef.co/go/structlayout",
 			"repository": "https://github.com/dominikh/go-structlayout",
 			"vcs": "",