added log/messages for nodes to notify the user when errors occur

This commit is contained in:
BroodjeAap 2022-10-02 15:08:21 +00:00
parent b46ebab40a
commit a0ef26cc68
7 changed files with 236 additions and 67 deletions

View file

@ -1,6 +1,9 @@
package main
import "time"
import (
"fmt"
"time"
)
type Watch struct {
ID uint `form:"watch_id" yaml:"watch_id"`
@ -21,6 +24,15 @@ type Filter struct {
Parents []*Filter `gorm:"-:all"`
Children []*Filter `gorm:"-:all"`
Results []string `gorm:"-:all"`
Logs []string `gorm:"-:all"`
}
func (filter *Filter) logf(format string, v ...any) {
filter.Logs = append(filter.Logs, fmt.Sprintf(format, v...))
}
func (filter *Filter) log(v ...any) {
filter.Logs = append(filter.Logs, fmt.Sprint(v...))
}
type FilterConnection struct {

View file

@ -141,7 +141,7 @@ func getFilterResult(filter *Filter, db *gorm.DB, urlCache map[string]string, us
}
}
default:
log.Println("getFilterResult called with filter.Type == ", filter.Type)
filter.log("getFilterResult called with filter.Type == ", filter.Type)
}
}
@ -155,13 +155,13 @@ func getFilterResultURL(filter *Filter, urlCache map[string]string, useCache boo
resp, err := http.Get(url)
if err != nil {
log.Println("Could not fetch url", url)
log.Println("Reason:", err)
filter.log("Could not fetch url", url)
filter.log("Reason:", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("Could not fetch url", url)
log.Println("Reason:", err)
filter.log("Could not fetch url", url)
filter.log("Reason:", err)
}
str := string(body)
filter.Results = append(filter.Results, str)
@ -171,15 +171,11 @@ func getFilterResultURL(filter *Filter, urlCache map[string]string, useCache boo
}
func getFilterResultXPath(filter *Filter) {
if filter.Parents == nil {
log.Println("Filter", filter.Name, "called without parents for", filter.Type)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
doc, err := htmlquery.Parse(strings.NewReader(result))
if err != nil {
log.Print(err)
filter.log(err)
continue
}
nodes, _ := htmlquery.QueryAll(doc, filter.Var1)
@ -193,10 +189,6 @@ func getFilterResultXPath(filter *Filter) {
}
func getFilterResultJSON(filter *Filter) {
if filter.Parents == nil {
log.Println("Filter", filter.Name, "called without parent for", filter.Type)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
for _, match := range gjson.Get(result, filter.Var1).Array() {
@ -207,20 +199,16 @@ func getFilterResultJSON(filter *Filter) {
}
func getFilterResultCSS(filter *Filter) {
if filter.Parents == nil {
log.Println("Filter", filter.Name, "called without parent for", filter.Type)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
doc, err := html.Parse(strings.NewReader(result))
if err != nil {
log.Print(err)
filter.log(err)
continue
}
sel, err := cascadia.Parse(filter.Var1)
if err != nil {
log.Print(err)
filter.log(err)
continue
}
for _, node := range cascadia.QueryAll(doc, sel) {
@ -233,17 +221,13 @@ func getFilterResultCSS(filter *Filter) {
}
func getFilterResultReplace(filter *Filter) {
if filter.Parents == nil {
log.Println("Filter", filter.Name, "called without parent for", filter.Type)
r, err := regexp.Compile(filter.Var1)
if err != nil {
filter.log(err)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
r, err := regexp.Compile(filter.Var1)
if err != nil {
log.Print(err)
continue
}
if filter.Var2 == nil {
filter.Results = append(filter.Results, r.ReplaceAllString(result, ""))
} else {
@ -254,10 +238,6 @@ func getFilterResultReplace(filter *Filter) {
}
func getFilterResultMatch(filter *Filter) {
if filter.Parents == nil {
log.Println("Filter", filter.Name, "called without parent for", filter.Type)
return
}
r, err := regexp.Compile(filter.Var1)
if err != nil {
log.Print(err)
@ -273,10 +253,6 @@ func getFilterResultMatch(filter *Filter) {
}
func getFilterResultSubstring(filter *Filter) {
if filter.Parents == nil {
log.Println("Filter", filter.Name, "called without parent for", filter.Type)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
substrings := strings.Split(filter.Var1, ",")
@ -287,6 +263,7 @@ func getFilterResultSubstring(filter *Filter) {
if strings.Contains(substring, ":") {
from_to := strings.Split(substring, ":")
if len(from_to) != 2 {
filter.log("Missing value in range: ", substring)
return
}
fromStr := from_to[0]
@ -297,6 +274,7 @@ func getFilterResultSubstring(filter *Filter) {
from64, err := strconv.ParseInt(fromStr, 10, 32)
var from = int(from64)
if hasFrom && err != nil {
filter.log("Could not parse left side of: ", substring)
return
} else if from < 0 {
from = len(asRunes) + from
@ -309,6 +287,7 @@ func getFilterResultSubstring(filter *Filter) {
to64, err := strconv.ParseInt(toStr, 10, 32)
var to = int(to64)
if hasTo && err != nil {
filter.log("Could not parse right side of: ", substring)
return
} else if to < 0 {
to = len(asRunes) + to
@ -323,6 +302,7 @@ func getFilterResultSubstring(filter *Filter) {
} else {
pos, err := strconv.ParseInt(substring, 10, 32)
if err != nil || pos < 0 {
filter.log("Could not parse: ", substring)
return
}
sb.WriteRune(asRunes[pos])
@ -339,6 +319,12 @@ func getFilterResultSum(filter *Filter) {
for _, result := range parent.Results {
if number, err := strconv.ParseFloat(result, 64); err == nil {
sum += number
} else {
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
}
}
}
@ -355,6 +341,12 @@ func getFilterResultMin(filter *Filter) {
min = number
setMin = true
}
} else {
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
}
}
}
@ -374,6 +366,12 @@ func getFilterResultMax(filter *Filter) {
max = number
setMax = true
}
} else {
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
}
}
}
@ -391,6 +389,12 @@ func getFilterResultAverage(filter *Filter) {
if number, err := strconv.ParseFloat(result, 64); err == nil {
sum += number
count++
} else {
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
}
}
}
@ -433,6 +437,12 @@ func getFilterResultRound(filter *Filter) {
if number, err := strconv.ParseFloat(result, 64); err == nil {
rounded := roundFloat(number, uint(decimals))
filter.Results = append(filter.Results, fmt.Sprintf("%f", rounded))
} else {
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
}
}
}
@ -485,12 +495,16 @@ func getFilterResultConditionLowerLast(filter *Filter, db *gorm.DB) {
} else {
lastValue, err := strconv.ParseFloat(previousOutput.Value, 64)
if err != nil {
log.Println("Could not convert previous value to number:", previousOutput.Value)
filter.log("Could not convert previous value to number:", previousOutput.Value)
continue
}
number, err := strconv.ParseFloat(result, 64)
if err != nil {
log.Println("Could not convert new value to number:", result)
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
continue
}
if number < lastValue {
@ -509,7 +523,7 @@ func getFilterResultConditionLowest(filter *Filter, db *gorm.DB) {
for _, previousOutput := range previousOutputs {
number, err := strconv.ParseFloat(previousOutput.Value, 64)
if err != nil {
log.Println("Could not convert result to number:", previousOutput.Value)
filter.log("Could not convert result to number:", previousOutput.Value)
continue
}
if number < lowest {
@ -523,7 +537,11 @@ func getFilterResultConditionLowest(filter *Filter, db *gorm.DB) {
for _, result := range parent.Results {
number, err := strconv.ParseFloat(result, 64)
if err != nil {
log.Println("Could not convert result to number:", result)
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
continue
}
if number < lowest {
@ -535,19 +553,23 @@ func getFilterResultConditionLowest(filter *Filter, db *gorm.DB) {
func getFilterResultConditionLowerThan(filter *Filter) {
if filter.Var2 == nil {
log.Println("No threshold given for Lower Than Filter")
filter.log("No threshold given")
return
}
threshold, err := strconv.ParseFloat(*filter.Var2, 64)
if err != nil {
log.Println("Could not convert convert threshold to number:", *filter.Var2)
filter.log("Could not convert convert threshold to number:", *filter.Var2)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
number, err := strconv.ParseFloat(result, 64)
if err != nil {
log.Println("Could not convert new value to number:", result)
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
continue
}
if number < threshold {
@ -567,12 +589,16 @@ func getFilterResultConditionHigherLast(filter *Filter, db *gorm.DB) {
} else {
lastValue, err := strconv.ParseFloat(previousOutput.Value, 64)
if err != nil {
log.Println("Could not convert previous value to number:", previousOutput.Value)
filter.log("Could not convert previous value to number:", previousOutput.Value)
continue
}
number, err := strconv.ParseFloat(result, 64)
if err != nil {
log.Println("Could not convert new value to number:", result)
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
continue
}
if number > lastValue {
@ -591,7 +617,7 @@ func getFilterResultConditionHighest(filter *Filter, db *gorm.DB) {
for _, previousOutput := range previousOutputs {
number, err := strconv.ParseFloat(previousOutput.Value, 64)
if err != nil {
log.Println("Could not convert result to number:", previousOutput.Value)
filter.log("Could not convert result to number:", previousOutput.Value)
continue
}
if number > highest {
@ -605,7 +631,11 @@ func getFilterResultConditionHighest(filter *Filter, db *gorm.DB) {
for _, result := range parent.Results {
number, err := strconv.ParseFloat(result, 64)
if err != nil {
log.Println("Could not convert result to number:", result)
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
continue
}
if number > highest {
@ -617,19 +647,23 @@ func getFilterResultConditionHighest(filter *Filter, db *gorm.DB) {
func getFilterResultConditionHigherThan(filter *Filter) {
if filter.Var2 == nil {
log.Println("No threshold given for Higher Than Filter")
filter.log("No threshold given for Higher Than Filter")
return
}
threshold, err := strconv.ParseFloat(*filter.Var2, 64)
if err != nil {
log.Println("Could not convert convert threshold to number:", *filter.Var2)
filter.log("Could not convert convert threshold to number:", *filter.Var2)
return
}
for _, parent := range filter.Parents {
for _, result := range parent.Results {
number, err := strconv.ParseFloat(result, 64)
if err != nil {
log.Println("Could not convert new value to number:", result)
if len(result) > 50 {
filter.log("Could not convert value, with length ", len(result), ", to number")
} else {
filter.log("Could not convert value, ", result, ", to number")
}
continue
}
if number > threshold {

View file

@ -273,9 +273,10 @@ var NewConnection = /** @class */ (function (_super) {
}(CanvasObject));
var DiagramNode = /** @class */ (function (_super) {
__extends(DiagramNode, _super);
function DiagramNode(id, x, y, label, meta, ctx, results) {
function DiagramNode(id, x, y, label, meta, ctx, results, logs) {
if (meta === void 0) { meta = {}; }
if (results === void 0) { results = new Array(); }
if (logs === void 0) { logs = new Array(); }
var _this = _super.call(this, x, y, 0, 0) || this;
_this.dragging = false;
_this.dragOrigin = new Point();
@ -287,10 +288,11 @@ var DiagramNode = /** @class */ (function (_super) {
_this.resize(ctx);
_this.deleteButton = new Button(0, 0, "Del", ctx, _diagram.deleteNodeCallback, _this);
_this.editButton = new Button(0, 0, "Edit", ctx, _diagram.editNodeCallback, _this);
_this.logButton = new Button(0, 0, "Log", ctx, _diagram.editNodeCallback, _this);
_this.logButton = new Button(0, 0, "Log", ctx, _diagram.logNodeCallback, _this);
_this.input = new NodeIO(_this, true);
_this.output = new NodeIO(_this, false);
_this.results = results;
_this.logs = logs;
return _this;
}
DiagramNode.prototype.update = function (ms) {
@ -359,6 +361,15 @@ var DiagramNode = /** @class */ (function (_super) {
this.logButton.draw(ctx, ms);
this.input.draw(ctx, ms);
this.output.draw(ctx, ms);
if (this.logs.length > 0) {
ctx.moveTo(this.x + 21, this.y + 6);
ctx.fillStyle = "orange";
ctx.beginPath();
ctx.lineTo(this.x + 23, this.y + 21);
ctx.lineTo(this.x + 6, this.y + 21);
ctx.lineTo(this.x + 14, this.y + 6);
ctx.fill();
}
ctx.strokeStyle = "#8E8E8E";
ctx.lineWidth = 3;
ctx.strokeRect(ms.offset.x + this.x, ms.offset.y + this.y, this.width, this.height);
@ -469,9 +480,10 @@ var MouseState = /** @class */ (function () {
return MouseState;
}());
var Diagrams = /** @class */ (function () {
function Diagrams(canvasId, editNodeCallback, deleteNodeCallback) {
function Diagrams(canvasId, editNodeCallback, deleteNodeCallback, logNodeCallback) {
if (editNodeCallback === void 0) { editNodeCallback = function () { }; }
if (deleteNodeCallback === void 0) { deleteNodeCallback = function () { }; }
if (logNodeCallback === void 0) { logNodeCallback = function () { }; }
this.shouldTick = true;
this.nodes = new Map();
this.connections = new Array();
@ -482,6 +494,7 @@ var Diagrams = /** @class */ (function () {
this.newConnection = null;
this.scale = 1.0;
this.editNodeCallback = function () { };
this.logNodeCallback = function () { };
this.deleteNodeCallback = function () { };
this.canvas = document.getElementById(canvasId);
if (this.canvas === null) {
@ -494,6 +507,7 @@ var Diagrams = /** @class */ (function () {
_diagram = this;
this.ctx = ctx;
this.editNodeCallback = editNodeCallback;
this.logNodeCallback = logNodeCallback;
this.deleteNodeCallback = deleteNodeCallback;
this.canvas.onmousemove = diagramOnMouseMove;
this.canvas.onmousedown = diagramOnMouseDown;
@ -625,10 +639,11 @@ var Diagrams = /** @class */ (function () {
};
Diagrams.prototype.draw = function () {
};
Diagrams.prototype.addNode = function (id, x, y, label, meta, results) {
Diagrams.prototype.addNode = function (id, x, y, label, meta, results, logs) {
if (meta === void 0) { meta = {}; }
if (results === void 0) { results = new Array(); }
var node = new DiagramNode(id, x, y, label, meta, this.ctx, results);
if (logs === void 0) { logs = new Array(); }
var node = new DiagramNode(id, x, y, label, meta, this.ctx, results, logs);
this.nodes.set(id, node);
};
Diagrams.prototype.addConnection = function (A, B) {

View file

@ -311,6 +311,7 @@ class DiagramNode extends CanvasObject {
meta: Object = {};
results: Array<string>;
logs: Array<string>;
constructor(
id: number,
@ -320,6 +321,7 @@ class DiagramNode extends CanvasObject {
meta: Object = {},
ctx: CanvasRenderingContext2D,
results: Array<string> = new Array(),
logs: Array<string> = new Array(),
){
super(x, y, 0, 0)
this.id = id;
@ -330,11 +332,12 @@ class DiagramNode extends CanvasObject {
this.deleteButton = new Button(0, 0, "Del", ctx, _diagram.deleteNodeCallback, this);
this.editButton = new Button(0, 0, "Edit", ctx, _diagram.editNodeCallback, this);
this.logButton = new Button(0, 0, "Log", ctx, _diagram.editNodeCallback, this);
this.logButton = new Button(0, 0, "Log", ctx, _diagram.logNodeCallback, this);
this.input = new NodeIO(this, true);
this.output = new NodeIO(this, false);
this.results = results;
this.logs = logs;
}
update(ms: MouseState) {
@ -346,7 +349,7 @@ class DiagramNode extends CanvasObject {
if (this.hover){
this.deleteButton.update(ms);
this.editButton.update(ms);
this.logButton.update(ms)
this.logButton.update(ms);
let onButtons = this.deleteButton.hover || this.editButton.hover || this.logButton.hover;
if (!this.dragging && ms.leftDown && !ms.draggingNode && !ms.draggingConnection && !onButtons){
this.dragging = true;
@ -411,6 +414,16 @@ class DiagramNode extends CanvasObject {
this.input.draw(ctx, ms);
this.output.draw(ctx, ms);
if(this.logs.length > 0){
ctx.moveTo(this.x + 21, this.y + 6);
ctx.fillStyle = "orange";
ctx.beginPath();
ctx.lineTo(this.x + 23, this.y + 21);
ctx.lineTo(this.x + 6, this.y + 21);
ctx.lineTo(this.x + 14, this.y + 6);
ctx.fill();
}
ctx.strokeStyle = "#8E8E8E";
ctx.lineWidth = 3;
@ -550,12 +563,14 @@ class Diagrams {
scale: number = 1.0;
editNodeCallback: (node: DiagramNode) => void = function (){};
logNodeCallback: (node: DiagramNode) => void = function (){};
deleteNodeCallback: (node: DiagramNode) => void = function (){};
constructor(
canvasId: string,
editNodeCallback: (node: DiagramNode) => void = function (){},
deleteNodeCallback: (node: DiagramNode) => void = function (){}
deleteNodeCallback: (node: DiagramNode) => void = function (){},
logNodeCallback: (node: DiagramNode) => void = function (){},
){
this.canvas = document.getElementById(canvasId) as HTMLCanvasElement;
if (this.canvas === null){
@ -568,6 +583,7 @@ class Diagrams {
_diagram = this;
this.ctx = ctx;
this.editNodeCallback = editNodeCallback;
this.logNodeCallback = logNodeCallback;
this.deleteNodeCallback = deleteNodeCallback;
this.canvas.onmousemove = diagramOnMouseMove;
@ -659,8 +675,16 @@ class Diagrams {
}
addNode(id: number, x: number, y: number, label: string, meta: Object = {}, results: Array<string> = new Array()){
let node = new DiagramNode(id, x, y, label, meta, this.ctx, results);
addNode(
id: number,
x: number,
y: number,
label: string,
meta: Object = {},
results: Array<string> = new Array(),
logs: Array<string> = new Array()
){
let node = new DiagramNode(id, x, y, label, meta, this.ctx, results, logs);
this.nodes.set(id, node);
}

View file

@ -562,6 +562,35 @@ function editNode(node) {
submitButton.innerHTML = "Save";
submitButton.onclick = function () { submitEditNode(node); };
}
function logNode(node) {
var e_1, _a;
var logButton = document.getElementById("logButton");
logButton.click();
var logTitle = document.getElementById("logModalLabel");
logTitle.innerHTML = node.label;
var logBody = document.getElementById("logTableBody");
logBody.innerHTML = "";
var logTable = document.getElementById("logTable");
try {
for (var _b = __values(node.logs), _c = _b.next(); !_c.done; _c = _b.next()) {
var log = _c.value;
var row = document.createElement("tr");
var cell = document.createElement("td");
var code = document.createElement("code");
code.innerHTML = log;
cell.appendChild(code);
row.appendChild(cell);
logBody.appendChild(row);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
}
function deleteNode(node) {
_diagram.nodes["delete"](node.id);
for (var i = 0; i < _diagram.connections.length; i++) {
@ -593,7 +622,7 @@ function submitEditNode(node) {
node.resize(_diagram.ctx);
}
function saveWatch() {
var e_1, _a, e_2, _b;
var e_2, _a, e_3, _b;
var watchIdInput = document.getElementById("watch_id");
var watchId = Number(watchIdInput.value);
var filters = new Array();
@ -617,12 +646,12 @@ function saveWatch() {
});
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
finally { if (e_2) throw e_2.error; }
}
var filtersInput = document.getElementById("filtersInput");
filtersInput.value = JSON.stringify(filters);
@ -639,12 +668,12 @@ function saveWatch() {
});
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e["return"])) _b.call(_e);
}
finally { if (e_2) throw e_2.error; }
finally { if (e_3) throw e_3.error; }
}
var connectionsInput = document.getElementById("connectionsInput");
connectionsInput.value = JSON.stringify(connections);

View file

@ -576,6 +576,26 @@ function editNode(node: DiagramNode){
submitButton.onclick = function() {submitEditNode(node);}
}
function logNode(node: DiagramNode){
let logButton = document.getElementById("logButton") as HTMLButtonElement;
logButton.click();
let logTitle = document.getElementById("logModalLabel") as HTMLHeadElement;
logTitle.innerHTML = node.label;
let logBody = document.getElementById("logTableBody") as HTMLElement;
logBody.innerHTML = "";
let logTable = document.getElementById("logTable") as HTMLTableElement;
for (let log of node.logs){
let row = document.createElement("tr");
let cell = document.createElement("td");
let code = document.createElement("code");
code.innerHTML = log;
cell.appendChild(code);
row.appendChild(cell);
logBody.appendChild(row);
}
}
function deleteNode(node: DiagramNode){
_diagram.nodes.delete(node.id)
for (let i = 0; i < _diagram.connections.length; i++){

View file

@ -29,6 +29,11 @@
Save Watch
</button>
</td>
<td id="OpenLogTD" hidden>
<button type="button" id="logButton" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LogModal">
Open Log
</button>
</td>
</tr>
</table>
@ -133,6 +138,31 @@
</div>
</div>
<div class="modal fade" id="LogModal" tabindex="-1" aria-labelledby="LogModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="logModalLabel">-</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<table class="table" id="logTable">
<thead>
<tr>
<th>Message</th>
</tr>
</thead>
<tbody id="logTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{ end }}
@ -141,7 +171,7 @@
<script>
var diagrams;
function canvasInit() {
diagrams = new Diagrams("canvas", editNode, deleteNode);
diagrams = new Diagrams("canvas", editNode, deleteNode, logNode);
{{ range .Filters }}
diagrams.addNode(
{{ .ID }},
@ -156,7 +186,12 @@ function canvasInit() {
},
[
{{ range .Results }}
["{{ . }}"],
'{{ . }}',
{{ end }}
],
[
{{ range.Logs }}
'{{ . }}',
{{ end }}
]
);