go-hep.org/x/hep@v0.38.1/groot/cmd/root-srv/data.go (about) 1 // Copyright ©2017 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "net/http" 12 stdpath "path" 13 "strings" 14 15 "gonum.org/v1/plot/vg" 16 17 "go-hep.org/x/hep/groot/riofs" 18 "go-hep.org/x/hep/groot/root" 19 "go-hep.org/x/hep/groot/rsrv" 20 "go-hep.org/x/hep/groot/rtree" 21 ) 22 23 const ( 24 plotH1 = "h1" 25 plotH2 = "h2" 26 plotS2 = "s2" 27 plotBranch = "branch" 28 ) 29 30 type plot struct { 31 Type string `json:"type"` 32 URI string `json:"uri"` 33 Dir string `json:"dir"` 34 Obj string `json:"obj"` 35 Vars []string `json:"vars"` 36 37 Options rsrv.PlotOptions `json:"options"` 38 } 39 40 type plotRequest struct { 41 cookie *http.Cookie 42 req plot 43 resp chan plotResponse 44 } 45 46 type plotResponse struct { 47 err error 48 ctype string 49 status int 50 body []byte 51 } 52 53 type jsNode struct { 54 ID string `json:"id,omitempty"` 55 URI string `json:"uri,omitempty"` 56 Dir string `json:"dir,omitempty"` 57 Obj string `json:"obj,omitempty"` 58 Text string `json:"text,omitempty"` 59 Icon string `json:"icon,omitempty"` 60 State struct { 61 Opened bool `json:"opened,omitempty"` 62 Disabled bool `json:"disabled,omitempty"` 63 Selected bool `json:"selected,omitempty"` 64 } `json:"state,omitempty"` 65 Children []jsNode `json:"children,omitempty"` 66 LiAttr jsAttr `json:"li_attr,omitempty"` 67 Attr jsAttr `json:"a_attr,omitempty"` 68 } 69 70 type jsAttr map[string]any 71 72 type brancher interface { 73 Branches() []rtree.Branch 74 } 75 76 type jsNodes []jsNode 77 78 func (p jsNodes) Len() int { return len(p) } 79 func (p jsNodes) Less(i, j int) bool { return p[i].ID < p[j].ID } 80 func (p jsNodes) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 81 82 func newJsNodes(bres brancher, parent jsNode) ([]jsNode, error) { 83 var err error 84 branches := bres.Branches() 85 if len(branches) <= 0 { 86 return nil, err 87 } 88 var nodes []jsNode 89 for _, b := range branches { 90 id := parent.ID 91 bid := strings.Join([]string{id, b.Name()}, "/") 92 node := jsNode{ 93 ID: bid, 94 URI: parent.URI, 95 Dir: stdpath.Join(parent.Dir, parent.Obj), 96 Obj: b.Name(), 97 Text: b.Name(), 98 Icon: "fa fa-leaf", 99 } 100 node.Attr, err = attrFor(b.(root.Object), node) 101 if err != nil { 102 return nil, err 103 } 104 node.Children, err = newJsNodes(b, node) 105 if err != nil { 106 return nil, err 107 } 108 nodes = append(nodes, node) 109 } 110 return nodes, nil 111 } 112 113 func fileJsTree(f *riofs.File, fname string) ([]jsNode, error) { 114 root := jsNode{ 115 ID: f.Name(), 116 URI: fname, 117 Dir: "/", 118 Text: fmt.Sprintf("%s (version=%v)", fname, f.Version()), 119 Icon: "fa fa-file", 120 } 121 root.State.Opened = true 122 return dirTree(f, fname, root) 123 } 124 125 func dirTree(dir riofs.Directory, path string, root jsNode) ([]jsNode, error) { 126 var nodes []jsNode 127 for _, k := range dir.Keys() { 128 obj, err := k.Object() 129 if err != nil { 130 return nil, fmt.Errorf("failed to extract key %q: %w", k.Name(), err) 131 } 132 switch obj := obj.(type) { 133 case rtree.Tree: 134 tree := obj 135 node := jsNode{ 136 ID: strings.Join([]string{path, k.Name()}, "/"), 137 URI: root.URI, 138 Dir: stdpath.Join(root.Dir, root.Obj), 139 Obj: k.Name(), 140 Text: fmt.Sprintf("%s (entries=%d)", k.Name(), tree.Entries()), 141 Icon: "fa fa-tree", 142 } 143 node.Children, err = newJsNodes(tree, node) 144 if err != nil { 145 return nil, err 146 } 147 nodes = append(nodes, node) 148 case riofs.Directory: 149 dir := obj 150 node := jsNode{ 151 ID: strings.Join([]string{path, k.Name()}, "/"), 152 URI: root.URI, 153 Dir: stdpath.Join(root.Dir, root.Obj), 154 Obj: k.Name(), 155 Text: k.Name(), 156 Icon: "fa fa-folder", 157 } 158 node.Children, err = dirTree(dir, path+"/"+k.Name(), node) 159 if err != nil { 160 return nil, err 161 } 162 node.Children = node.Children[0].Children 163 nodes = append(nodes, node) 164 default: 165 id := strings.Join([]string{path, k.Name() + fmt.Sprintf(";%d", k.Cycle())}, "/") 166 node := jsNode{ 167 ID: id, 168 URI: root.URI, 169 Dir: stdpath.Join(root.Dir, root.Obj), 170 Obj: k.Name(), 171 Text: fmt.Sprintf("%s;%d", k.Name(), k.Cycle()), 172 Icon: iconFor(obj), 173 } 174 node.Attr, err = attrFor(obj, node) 175 if err != nil { 176 return nil, err 177 } 178 nodes = append(nodes, node) 179 } 180 } 181 root.Children = nodes 182 return []jsNode{root}, nil 183 } 184 185 func iconFor(obj root.Object) string { 186 cls := obj.Class() 187 switch { 188 case strings.HasPrefix(cls, "TH1"): 189 return "fa fa-bar-chart-o" 190 case strings.HasPrefix(cls, "TH2"): 191 return "fa fa-bar-chart-o" 192 case strings.HasPrefix(cls, "TGraph"): 193 return "fa fa-bar-chart-o" 194 } 195 return "fa fa-cube" 196 } 197 198 func attrFor(obj root.Object, node jsNode) (jsAttr, error) { 199 cmd := new(bytes.Buffer) 200 cls := obj.Class() 201 switch { 202 case strings.HasPrefix(cls, "TH1"): 203 req := plot{ 204 Type: plotH1, 205 URI: node.URI, 206 Dir: node.Dir, 207 Obj: node.Obj, 208 Options: rsrv.PlotOptions{ 209 Title: node.Obj, 210 Type: "svg", 211 Height: -1, 212 Width: 20 * vg.Centimeter, 213 }, 214 } 215 err := json.NewEncoder(cmd).Encode(req) 216 if err != nil { 217 return nil, err 218 } 219 return jsAttr{ 220 "plot": true, 221 "href": "/plot", 222 "cmd": cmd.String(), 223 }, nil 224 case strings.HasPrefix(cls, "TH2"): 225 req := plot{ 226 Type: plotH2, 227 URI: node.URI, 228 Dir: node.Dir, 229 Obj: node.Obj, 230 Options: rsrv.PlotOptions{ 231 Title: node.Obj, 232 Type: "svg", 233 Height: -1, 234 Width: 20 * vg.Centimeter, 235 }, 236 } 237 err := json.NewEncoder(cmd).Encode(req) 238 if err != nil { 239 return nil, err 240 } 241 return jsAttr{ 242 "plot": true, 243 "href": "/plot", 244 "cmd": cmd.String(), 245 }, nil 246 case strings.HasPrefix(cls, "TGraph"): 247 req := plot{ 248 Type: plotS2, 249 URI: node.URI, 250 Dir: node.Dir, 251 Obj: node.Obj, 252 Options: rsrv.PlotOptions{ 253 Title: node.Obj, 254 Type: "svg", 255 Height: -1, 256 Width: 20 * vg.Centimeter, 257 }, 258 } 259 err := json.NewEncoder(cmd).Encode(req) 260 if err != nil { 261 return nil, err 262 } 263 return jsAttr{ 264 "plot": true, 265 "href": "/plot", 266 "cmd": cmd.String(), 267 }, nil 268 case strings.HasPrefix(cls, "TBranch"): 269 req := plot{ 270 Type: plotBranch, 271 URI: node.URI, 272 Dir: stdpath.Dir(node.Dir), 273 Obj: stdpath.Base(node.Dir), 274 Vars: []string{node.Obj}, 275 Options: rsrv.PlotOptions{ 276 Title: node.Obj, 277 Type: "svg", 278 Height: -1, 279 Width: 20 * vg.Centimeter, 280 }, 281 } 282 err := json.NewEncoder(cmd).Encode(req) 283 if err != nil { 284 return nil, err 285 } 286 return jsAttr{ 287 "plot": true, 288 "href": "/plot", 289 "cmd": cmd.String(), 290 }, nil 291 } 292 // TODO(sbinet) do something clever with things we don't know how to handle? 293 return nil, nil 294 }