@@ 1,9 1,9 @@
package main
-// TODO: Report-to (e.g., show anchor person's superior)
+// TODO: Coloring by regexp
// TODO: Support for ordering
// TODO: Max-depth (e.g., show only X levels below anchor person)
-// TODO: Dotted lines
+// TODO: Dotted line report-to
import (
"encoding/csv"
@@ 40,24 40,25 @@ var boxH int
* Main *
**************************************************************************/
func main() {
- c := flag.String("c", "", "CSV input file")
- o := flag.String("f", "", "Filter for these people's organization (regex)")
- t := flag.String("t", "", "Display column numbers, indexed by 0, separated by commas; empty cells are omitted; badly formatted numbers are ignored")
+ csv := flag.String("c", "", "CSV input file")
+ fltr := flag.String("f", "", "Filter for these people's organization (regex)")
+ coln := flag.String("t", "", "Display column numbers, indexed by 0, separated by commas; empty cells are omitted; badly formatted numbers are ignored")
+ sup := flag.Bool("s", false, "Display superior; only used if results are filtered")
flag.Parse()
- if *c == "" {
+ if *csv == "" {
fmt.Println("CSV is required")
flag.Usage()
os.Exit(1)
}
- f, e := os.Open(*c)
- defer f.Close()
- if e != nil {
- fmt.Println(e.Error())
+ fin, err := os.Open(*csv)
+ defer fin.Close()
+ if err != nil {
+ fmt.Println(err.Error())
os.Exit(1)
}
var cols []int
- if *t != "" {
- cols_s := strings.Split(*t, ",")
+ if *coln != "" {
+ cols_s := strings.Split(*coln, ",")
cols = make([]int, len(cols_s))
for i, k := range cols_s {
v, e := strconv.Atoi(k)
@@ 68,16 69,26 @@ func main() {
}
}
}
- ps, e := makePeople(f, cols)
- if e != nil {
- fmt.Println(e.Error())
+ ps, err := makePeople(fin, cols)
+ if err != nil {
+ fmt.Println(err.Error())
os.Exit(1)
}
- if *o != "" {
- ps = filter(ps, *o)
+ if *fltr != "" {
+ ps = filter(ps, *fltr)
}
rs := rank(ps)
- renderSVG(os.Stdout, rs)
+ if *sup {
+ found := false
+ for _, k := range rs[0] {
+ if k.Manager != nil {
+ found = true
+ break
+ }
+ }
+ *sup = *sup && found
+ }
+ renderSVG(os.Stdout, rs, *sup)
}
/**************************************************************************
@@ 142,12 153,15 @@ func makePeople(f io.Reader, cols []int)
m, ok := ps[mgrName]
if !ok {
m = New(mgrName)
+ m.LineMask = mask
ps[mgrName] = m
}
p.SetManager(m)
}
- if strings.Contains(l[2], "Contractor") {
- p.Contractor = true
+ if len(l) > 2 {
+ if strings.Contains(l[2], "Contractor") {
+ p.Contractor = true
+ }
}
p.Lines = make([]string, len(cols))
for i, k := range cols {
@@ 27,7 27,7 @@ func (b Box) in() (int, int) {
return b.X + boxW/2, b.Y
}
-func renderSVG(out io.Writer, rs Ranks) {
+func renderSVG(out io.Writer, rs Ranks, sup bool) {
boxH = 30
ty := textSize + textPY
for _, b := range rs[0][0].LineMask {
@@ 38,6 38,9 @@ func renderSVG(out io.Writer, rs Ranks)
c := svg.New(out)
pageWidth := rs.width()
h := rs.height()
+ if sup {
+ h += boxH + gapH + (marginH * 2)
+ }
c.Start(pageWidth, h)
c.Filter("blur")
c.FeGaussianBlur(svg.Filterspec{In: "SourceAlpha", Result: "blur"}, 5, 5)
@@ 50,15 53,19 @@ func renderSVG(out io.Writer, rs Ranks)
c.Rect(0, 0, boxW, boxH, "fill: inherit; stroke: black; stroke-width: 1;")
c.Gend()
c.Gid(_root_id)
- c.Rect(0, 0, boxW, boxH, "fill: inherit; stroke: black; stroke-width: 3;")
+ c.Rect(0, 0, boxW, boxH, "fill: inherit; stroke: black; stroke-width: 5;")
+ c.Rect(0, 0, boxW, boxH, "fill: none; stroke: white; stroke-width: 1;")
c.Gend()
c.DefEnd()
defer c.End()
b := make(map[string]Box)
xoff := marginW
+ if sup {
+ rs = addSuperiors(rs)
+ }
for _, p := range rs[0] {
- drawOrg(c, xoff, marginH/2, p, b, true, false)
+ drawOrg(c, xoff, marginH/2, p, b, true, false, sup)
xoff += p.Width()
}
for _, r := range rs {
@@ 66,7 73,29 @@ func renderSVG(out io.Writer, rs Ranks)
}
}
-func drawOrg(canvas *svg.SVG, xoffset, yoffset int, p *Person, boxes map[string]Box, root, leaf bool) {
+func addSuperiors(rs Ranks) Ranks {
+ r := Rank{}
+ for _, p := range rs[0] {
+ s := p.Manager
+ if r.contains(s) {
+ s.Reports = append(s.Reports, p)
+ } else {
+ s.Reports = []*Person{p}
+ r = append(r, s)
+ }
+ }
+ rv := make(Ranks, len(rs)+1)
+ rv[0] = r
+ for i, n := range rs {
+ rv[i+1] = n
+ }
+ return rv
+}
+
+func drawOrg(canvas *svg.SVG, xoffset, yoffset int, p *Person, boxes map[string]Box, root, leaf, sup bool) {
+ if sup {
+ root = false
+ }
drawPerson(canvas, xoffset, yoffset, p.Index(), p, boxes, root, leaf)
if len(p.Reports) > 0 {
isLeaf := true
@@ 75,7 104,12 @@ func drawOrg(canvas *svg.SVG, xoffset, y
}
yoffset += boxH + (gapH / 2)
for _, q := range p.Reports {
- drawOrg(canvas, xoffset, yoffset, q, boxes, false, isLeaf)
+ // Using sup for root looks wierd, but what it's saying is: if we just
+ // drew the superior (sup == true), then what we really want to
+ // highlight is this person's reports, so pass in true for root. In
+ // any case, pass in false for sup -- and if it wasn't the superior
+ // (sup == false), then don't highlight the reports.
+ drawOrg(canvas, xoffset, yoffset, q, boxes, sup, isLeaf, false)
if isLeaf {
yoffset += boxH + (gapH / 2)
} else {
@@ 106,14 140,14 @@ func drawBox(c *svg.SVG, x, y int, p *Pe
c.Use(x, y, ref, fill)
toff := x + (boxW / 2)
toffY := y + textSize + textPY
- if p.LineMask[0] {
+ if len(p.LineMask) > 0 && p.LineMask[0] && len(p.Lines) > 0 {
c.Text(toff, y+textSize+textPY, p.Lines[0], "text-anchor: middle; font-size: smaller;")
toffY += textSize + textPY
}
c.Text(toff, toffY, p.Name, "text-anchor: middle; font-weight: bold;")
toffY += textSize + textPY
for i, k := range p.LineMask {
- if i == 0 {
+ if i == 0 || i >= len(p.Lines) {
continue
}
if k {