From a81804b0f64f3b1c4881467891c2c17e35a9299c Mon Sep 17 00:00:00 2001 From: dcore94 Date: Wed, 1 Feb 2023 16:07:15 +0100 Subject: [PATCH] support for prov-o exports and completed visual revision to be homogeneous with method list --- ccp/js/executionhistorycontroller.js | 345 ++++++++++++++++++--------- 1 file changed, 234 insertions(+), 111 deletions(-) diff --git a/ccp/js/executionhistorycontroller.js b/ccp/js/executionhistorycontroller.js index b3bc15f..0b11e66 100644 --- a/ccp/js/executionhistorycontroller.js +++ b/ccp/js/executionhistorycontroller.js @@ -1,11 +1,13 @@ class CCPExecutionHistory extends HTMLElement { - + #boot = null; #rootdoc = null; #serviceurl = null; #broadcasturl = null; - #executions = []; + #data = []; + #filtered = []; #socket = null; + #searchfield = null; constructor(){ super() @@ -22,12 +24,12 @@ class CCPExecutionHistory extends HTMLElement { } connectedCallback(){ - this.#rootdoc.innerHTML = this.render() + this.render() this.refreshExecutions() } render(){ - return ` + this.#rootdoc.innerHTML = ` -
    +
    +
    +
    +
    + Execution history +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
      +
      +
      +
      ` + this.#rootdoc.querySelector("button[name=refresh]").addEventListener("click", ev=>{ + this.refreshExecutions() + }) + + this.#searchfield = this.#rootdoc.querySelector("input[name=search]") + this.#searchfield.addEventListener("input", ev=>{ + this.updateList() + }) + } + + updateList(){ + const filter = this.#searchfield.value + if(filter === "" || filter == null || filter == undefined){ + this.#filtered = this.#data + }else{ + const f = filter.toLowerCase() + this.#filtered = this.#data.filter(d=>{ + return false || + (d.status.toLowerCase().indexOf(f) !== -1)|| + (d.method.indexOf(f) !== -1) + }) + } + this.groupBy() + BSS.apply(this.#execution_list_bss, this.#rootdoc) + } + + groupBy(){ + this.#filtered = this.#filtered.reduce((catalog, exec)=>{ + const category = exec.method + catalog[category] = catalog[category] ?? [] + catalog[category].push(exec) + return catalog + }, {}) } refreshExecution(id){ @@ -92,16 +178,16 @@ class CCPExecutionHistory extends HTMLElement { else throw `Unable to load execution ${id}` }).then(data=>{ const exec = data[0] - for(var i=0; i < this.#executions.length; i++){ - if(this.#executions[i].id == exec.id){ - this.#executions[i] = exec + for(var i=0; i < this.#data.length; i++){ + if(this.#data[i].id == exec.id){ + this.#data[i] = exec break } - if(i === this.#executions.length){ - this.#executions = data.concat(this.#executions) + if(i === this.#data.length){ + this.#data = data.concat(this.#data) } } - BSS.apply(this.execution_list_bss, this.#rootdoc) + this.updateList() }).catch(err=>{ console.error(err)}) } @@ -117,14 +203,14 @@ class CCPExecutionHistory extends HTMLElement { if(reply.status === 200) return reply.json(); else throw "Unable to load executions" }).then(data=>{ - this.#executions = data - BSS.apply(this.execution_list_bss, this.#rootdoc) + this.#data = data + this.updateList() }).catch(err=>{ console.error(err)}) } connectNewExecution(){ document.addEventListener("newexecution", ev=>{ - this.#executions = [ {id: ev.detail } ].concat(this.#executions) + this.#data = [ {id: ev.detail } ].concat(this.#data) }) } @@ -132,7 +218,7 @@ class CCPExecutionHistory extends HTMLElement { this.#socket = new WebSocket(this.#broadcasturl + "/executions"); this.#socket.onmessage = event=>{ const data = JSON.parse(event.data) - let exec = this.#executions.filter(e=>e.id === data.jobID)[0] + let exec = this.#data.filter(e=>e.id === data.jobID)[0] if(exec){ this.refreshExecution(exec.id) } @@ -159,17 +245,17 @@ class CCPExecutionHistory extends HTMLElement { }).catch(err => console.error(err)) } - downloadZIP(id){ + export(id, mime, filename){ this.#boot.secureFetch(`${this.#serviceurl}/executions/${id}`, - { method: "GET", headers : { "Accept" : "application/zip"} }).then(reply =>{ + { method: "GET", headers : { "Accept" : mime} }).then(reply =>{ if (reply.status !== 200) { - throw "Unable to download" + throw "Unable to export " + mime } return reply.blob() }).then(blob => { const objectURL = URL.createObjectURL(blob) var tmplnk = document.createElement("a") - tmplnk.download = id + ".zip" + tmplnk.download = filename tmplnk.href = objectURL document.body.appendChild(tmplnk) tmplnk.click() @@ -177,33 +263,20 @@ class CCPExecutionHistory extends HTMLElement { }).catch(err=>{ console.error(err)}) } - execution_list_bss = { + #execution_list_bss = { template : "#EXECUTIOM_LIST_TEMPLATE", - target : "ol[name=ccp_execution_list]", - in : ()=>{ return {executions : this.#executions} }, + target : "ul[name=ccp_execution_list]", + in : ()=>this, recurse:[ { - target : "li", - in : (e,d)=>d.executions, - apply: (e,d,i)=>e.setAttribute("data-index", i), - on_click: ev=>{ - if(ev.target.getAttribute("name") === "delete"){ - if(window.confirm("Confirm deletion of this execution?")){ - const index = Number(ev.currentTarget.getAttribute("data-index")) - this.deleteExecution(this.#executions[index].id) - } - } - if(ev.target.getAttribute("name") === "zip"){ - const index = Number(ev.currentTarget.getAttribute("data-index")) - this.downloadZIP(this.#executions[index].id) - } - }, - recurse : [ + target : "li.ccp-method-item", + "in" : (e,d)=>Object.keys(this.#filtered), + recurse: [ { - target : "details", + target : "details[name=level1]", apply : (e,d)=>{ - e.alt = e.title = d.id - if(sessionStorage.getItem(d.id) === "open") e.open = "open"; + e.alt = e.title = d + if(sessionStorage.getItem(d) === "open") e.open = "open"; else e.removeAttribute("open"); }, on_toggle : ev=>{ @@ -212,68 +285,118 @@ class CCPExecutionHistory extends HTMLElement { }else{ sessionStorage.removeItem(ev.currentTarget.alt) } - } + }, }, { - target : "span[name=runtime]", - apply : (e,d)=>{ - e.classList.add(d.infrastructuretype) - e.title = e.alt = `${d.runtime} (image: ${d.runtimeimage}) on ${d.infrastructure}` - e.textContent = d.runtime - } + target : "summary.ccp-method-item-header h5", + apply : (e,d) => { e.textContent = d } }, { - target : "h5", - apply : (e,d)=>{ - e.textContent = `${d.method} v. ${d.methodversion}` - } + target : "summary.ccp-method-item-header span[name=accepted]", + apply : (e,d) => { e.textContent = this.#filtered[d].filter(x=>x.status === 'accepted').length } }, { - target : "span[name=updated]", - apply : (e,d)=>{ - const dt = new Date(d.updated) - e.textContent = `Last update ${dt.toLocaleDateString()} @ ${dt.toLocaleTimeString()}` - } + target : "summary.ccp-method-item-header span[name=failed]", + apply : (e,d) => { e.textContent = this.#filtered[d].filter(x=>x.status === 'failed').length } }, { - target : "span[name=status]", - apply : (e,d)=>{ - if(d.status){ - const status = d.status - e.textContent = status - if (status === "running") e.classList.add("badge-primary"); - else if (status === "successful") e.classList.add("badge-success"); - else if (status === "failure") e.classList.add("badge-danger"); - else e.classList.add("badge-secondary"); + target : "summary.ccp-method-item-header span[name=successful]", + apply : (e,d) => { e.textContent = this.#filtered[d].filter(x=>x.status === 'successful').length } + }, + { + target : "summary.ccp-method-item-header span[name=running]", + apply : (e,d) => { e.textContent = this.#filtered[d].filter(x=>x.status === 'running').length } + }, + { + target : "li.ccp-execution-item", + "in" : (e,d)=>this.#filtered[d], + apply : (e,d)=>e.setAttribute("data-index", d.id), + on_click: ev=>{ + if(ev.target.getAttribute("name") === "delete"){ + if(window.confirm("Confirm deletion of this execution?")){ + const id = ev.currentTarget.getAttribute("data-index") + this.deleteExecution(id) + } } - } - }, - { - target : "span[name=message]", - apply : (e,d)=>{ - if(d.message){ - e.textContent = d.message + if(ev.target.getAttribute("name") === "zip"){ + const id = ev.currentTarget.getAttribute("data-index") + this.export(id, "application/zip", id + ".zip") } - } - }, - { - target : "ul", + if(ev.target.getAttribute("name") === "provo"){ + const id = ev.currentTarget.getAttribute("data-index") + this.export(id, "application/prov-o+xml", id + ".xml") + } + }, recurse : [ { - target : "li", - "in" : (e,d)=>{ - return d.resources.map(l=>{ - return { href : this.#serviceurl + "/executions/" + d.id + "/" + l.path, path : l.path} - }) - }, - on_click : ev=>{ - const href = ev.currentTarget.bss_input.data.href - const name = ev.currentTarget.bss_input.data.path - this.download(href, name) - }, + target : "details", apply : (e,d)=>{ - e.innerHTML = `${d.path}` + e.alt = e.title = d.id + if(sessionStorage.getItem(d.id) === "open") e.open = "open"; + else e.removeAttribute("open"); + }, + on_toggle : ev=>{ + if(ev.target.open){ + sessionStorage.setItem(ev.currentTarget.alt, 'open') + }else{ + sessionStorage.removeItem(ev.currentTarget.alt) + } } + }, + { + target : "span[name=version]", + apply : (e,d)=>{ + e.textContent = `${d.methodversion}` + } + }, + { + target : "span[name=status]", + apply : (e,d)=>{ + if(d.status){ + const status = d.status + e.textContent = status + if (status === "running") e.classList.add("badge-primary"); + else if (status === "successful") e.classList.add("badge-success"); + else if (status === "failure") e.classList.add("badge-danger"); + else e.classList.add("badge-secondary"); + } + } + }, + { + target : "span[name=updated]", + apply : (e,d)=>{ + const dt = new Date(d.updated) + e.textContent = `Last update ${dt.toLocaleDateString()} @ ${dt.toLocaleTimeString()}` + } + }, + { + target : "span[name=message]", + apply : (e,d)=>{ + if(d.message){ + e.textContent = d.message + } + } + }, + { + target : "ul", + recurse : [ + { + target : "li", + "in" : (e,d)=>{ + return d.resources.map(l=>{ + return { href : this.#serviceurl + "/executions/" + d.id + "/" + l.path, path : l.path} + }) + }, + on_click : ev=>{ + const href = ev.currentTarget.bss_input.data.href + const name = ev.currentTarget.bss_input.data.path + this.download(href, name) + }, + apply : (e,d)=>{ + e.innerHTML = `${d.path}` + } + } + ] } ] }