Adds additional attribute support (titles, subtext1 and 2)


Comment
3 files changed, 62 insertions(+), 20 deletions(-)

M cmd/orgchart/main.go
M cmd/orgchart/person.go
M cmd/orgchart/svg.go
M cmd/orgchart/main.go +39 -15
@@ 1,8 1,9 @@ 
 package main
 
-// TODO: Addl attrs in boxes (title, etc.)
+// TODO: Report-to (e.g., show anchor person's superior)
+// TODO: Highlight filtered person boxes
+// TODO: Support for ordering
 // TODO: Max-depth (e.g., show only X levels below anchor person)
-// TODO: Report-to (e.g., show anchor person's superior)
 // TODO: Dotted lines
 
 import (

          
@@ 19,19 20,19 @@ import (
  *  Constants & global variables                                          *
  **************************************************************************/
 const (
-	boxW    = 170
-	gapW    = 30
-	marginW = 20
-	boxH    = 30
-	gapH    = 100
-	marginH = 20
-	textPX  = 5
-	textPY  = 20
-	_id     = "person"
-	_ref    = "#person"
+	boxW     = 190
+	gapW     = 30
+	marginW  = 20
+	gapH     = 100
+	marginH  = 20
+	textPX   = 5
+	textPY   = 5
+	textSize = 12
+	_id      = "person"
+	_ref     = "#person"
 )
 
-var pageWidth int
+var boxH int
 
 /**************************************************************************
  *  Main                                                                  *

          
@@ 39,6 40,9 @@ var pageWidth int
 func main() {
 	c := flag.String("c", "", "CSV input file")
 	o := flag.String("f", "", "Filter for these people's organization (regex)")
+	t := flag.Int("t", -1, "Title column (if not specified, omitted)")
+	s1 := flag.Int("s1", -1, "Subtext #1 column (if not specified, omitted)")
+	s2 := flag.Int("s2", -1, "Subtext #2 column (if not specified, omitted)")
 	flag.Parse()
 	if *c == "" {
 		fmt.Println("CSV is required")

          
@@ 51,7 55,7 @@ func main() {
 		fmt.Println(e.Error())
 		os.Exit(1)
 	}
-	ps, e := makePeople(f)
+	ps, e := makePeople(f, *t, *s1, *s2)
 	if e != nil {
 		fmt.Println(e.Error())
 		os.Exit(1)

          
@@ 60,6 64,17 @@ func main() {
 		ps = filter(ps, *o)
 	}
 	rs := rank(ps)
+	boxH = 30
+	ty := textSize + textPY
+	if *t > -1 {
+		boxH += ty
+	}
+	if *s1 > -1 {
+		boxH += ty
+	}
+	if *s2 > -1 {
+		boxH += ty
+	}
 	renderSVG(os.Stdout, rs)
 }
 

          
@@ 100,7 115,7 @@ func matches(p *Person, re *regexp.Regex
 
 // 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) (map[string]*Person, error) {
+func makePeople(f io.Reader, t, s1, s2 int) (map[string]*Person, error) {
 	r := csv.NewReader(f)
 	r.LazyQuotes = true
 	r.TrailingComma = true

          
@@ 128,6 143,15 @@ func makePeople(f io.Reader) (map[string
 		if strings.Contains(l[2], "Contractor") {
 			p.Contractor = true
 		}
+		if t > -1 {
+			p.Title = l[t]
+		}
+		if s1 > -1 {
+			p.Subtext1 = l[s1]
+		}
+		if s2 > -1 {
+			p.Subtext2 = l[s2]
+		}
 		ps[name] = p
 	}
 	if e == io.EOF {

          
M cmd/orgchart/person.go +3 -0
@@ 7,6 7,9 @@ import "fmt"
  **************************************************************************/
 type Person struct {
 	Name       string
+	Title      string
+	Subtext1   string
+	Subtext2   string
 	Manager    *Person
 	Reports    []*Person
 	Contractor bool

          
M cmd/orgchart/svg.go +20 -5
@@ 6,6 6,8 @@ import (
 	"github.com/ajstarks/svgo"
 )
 
+var pageWidth int
+
 /**************************************************************************
  *  SVG generating code                                                   *
  **************************************************************************/

          
@@ 77,19 79,32 @@ func drawPerson(canvas *svg.SVG, x, y, i
 	if !leaf {
 		x += (person.Width() - (boxW + (gapH / 2))) / 2
 	}
-	drawBox(canvas, x, y, person.Name, person.Contractor)
+	drawBox(canvas, x, y, person)
 	boxes[person.Name] = Box{X: x, Y: y, Leaf: leaf}
 }
 
-func drawBox(c *svg.SVG, x, y int, n string, yn bool) {
+func drawBox(c *svg.SVG, x, y int, p *Person) {
 	c.Use(x+2, y+3, _ref, `filter="url(#blur)"`)
 	fill := "fill: white"
-	if yn {
+	if p.Contractor {
 		fill = "fill: yellow"
 	}
 	c.Use(x, y, _ref, fill)
-	toff := x + ((boxW - textPX) / 2) + textPX
-	c.Text(toff, y+textPY, n, "text-anchor: middle;")
+	toff := x + (boxW / 2)
+	toffY := y + textSize + textPY
+	if p.Title != "" {
+		c.Text(toff, y+textSize+textPY, p.Title, "text-anchor: middle; font-size: smaller;")
+		toffY += textSize + textPY
+	}
+	c.Text(toff, toffY, p.Name, "text-anchor: middle; font-weight: bold;")
+	toffY += textSize + textPY
+	if p.Subtext1 != "" {
+		c.Text(toff, toffY, p.Subtext1, "text-anchor: middle; font-size: smaller;")
+		toffY += textSize + textPY
+	}
+	if p.Subtext2 != "" {
+		c.Text(toff, toffY, p.Subtext2, "text-anchor: middle; font-size: smaller;")
+	}
 }
 
 func connectBoxes(c *svg.SVG, ps Rank, bs map[string]Box) {