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  }