made archived view ooptional, refresh applies only to viewd tab, added delete for archived, improved labels, debugged

This commit is contained in:
dcore94 2024-06-11 16:14:00 +02:00
parent 9426adf4eb
commit 59f76372b7
1 changed files with 149 additions and 98 deletions

View File

@ -11,8 +11,8 @@ class CCPExecutionHistory extends HTMLElement {
#socket = null;
#interval = null;
#searchfield = null;
#fileupload = null;
#archiveupload = null;
// #fileupload = null;
// #archiveupload = null;
#ws = null;
#execfolderid = null
#archived = []
@ -39,7 +39,14 @@ class CCPExecutionHistory extends HTMLElement {
"direct_link_help" : "Navigate to the direct link for opening this exact execution in the exeuction form",
"confirm_import_link" : "Please confirm importing of execution from link",
"confirm_import_file" : "Please confirm importing of execution from file",
"confirm_delete_execution" : "Please confirm deletion of this execution"
"confirm_delete_execution" : "Please confirm deletion of this execution",
"confirm_delete_archived_execution" : "Please confirm deletion of this archived execution from your workspace.",
"live_executions" : "Live executions",
"archived_executions" : "Archived executions",
"restore_execution_help" : "Restore this archived execution back to CCP",
"infrastructure_help" : "The infrastructure where this execution has been scheduled on",
"runtime_help" : "The runtime where this execution has been scheduled on",
"loading_archived_help" : "Loading archived executions from workspace. This can take a while."
}
}
@ -63,13 +70,13 @@ class CCPExecutionHistory extends HTMLElement {
this.#broadcasturl = this.#serviceurl.replace(/^http/, "ws")
}
this.#broadcasturl = this.#broadcasturl + "/ws/notification"
this.#archive = this.getAttribute("archive")
this.#archive = this.getAttribute("allow-archive") && this.getAttribute("allow-archive").toLowerCase() === "true"
this.connectNewExecution()
this.connectBroadcastWithSubject()
this.render()
this.refreshExecutions()
this.refreshArchivedExecutions()
if(this.#archive) this.refreshArchivedExecutions();
}
render(){
@ -77,6 +84,10 @@ class CCPExecutionHistory extends HTMLElement {
<link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
.galaxy{
background-color: #2c3143;
color: yellow;
}
.lxd{
background-color: #dd4814;
color: white;
@ -149,8 +160,8 @@ class CCPExecutionHistory extends HTMLElement {
</p>
</summary>
<div class="d-flex flex-wrap" style="gap:3px; overflow:hidden">
<span style="text-overflow:ellipsis" class="badge badge-light text-info border border-info" name="infrastructure" alt="Infrastructure" title="Infrastructure"></span>
<span class="badge" name="runtime" alt="Runtime" title="Runtime"></span>
<span style="text-overflow:ellipsis" class="badge badge-light text-info border border-info" name="infrastructure" alt="${this.getLabel("infrastructure_help")}" title="${this.getLabel("infrastructure_help")}"></span>
<span class="badge" name="runtime" alt="${this.getLabel("runtime_help")}" title="${this.getLabel("runtime_help")}"></span>
</div>
<div name="logterminalcontainer" style="margin:5px 0 5px 0">
</div>
@ -168,7 +179,7 @@ class CCPExecutionHistory extends HTMLElement {
<option value="application/x-sh+curl" data-ext="sh" title="Generate Bash script (curl)">Bash (curl)</option>
<option value="application/x-sh+wget" data-ext="sh" title="Generate Bash script (wget)">Bash (wget)</option>
</select>
<button data-index="0" name="codegen" title="Generate code" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<button data-index="0" name="codegen" title="${this.getLabel("generate_code_help")}" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 96 960 960">
<path d="m384 721 43-43-101-102 101-101-43-43-144 144.5L384 721Zm192 0 145-145-144-144-43 43 101 101-102 102 43 43ZM180 936q-24.75 0-42.375-17.625T120 876V276q0-24.75 17.625-42.375T180 216h205q5-35 32-57.5t63-22.5q36 0 63 22.5t32 57.5h205q24.75 0 42.375 17.625T840 276v600q0 24.75-17.625 42.375T780 936H180Zm0-60h600V276H180v600Zm300-617q14 0 24.5-10.5T515 224q0-14-10.5-24.5T480 189q-14 0-24.5 10.5T445 224q0 14 10.5 24.5T480 259ZM180 876V276v600Z"/>
</svg>
@ -187,28 +198,30 @@ class CCPExecutionHistory extends HTMLElement {
</li>
</ul>
</template>
<template id="ARCHIVED_EXECUTIOM_LIST_TEMPLATE">
<template id="ARCHIVED_EXECUTION_LIST_TEMPLATE">
<ul name="ccp_archived_execution_list" class="ccp-execution-list list-group border border-2">
<li class="ccp-method-item list-group-item list-group-item-dark">
<li class="ccp-method-item ccp-archived-execution-method-item list-group-item list-group-item-dark">
<details name="level1">
<summary class="ccp-method-item-header noselect d-flex flex-wrap justify-content-between">
<h5 class="text-primary d-inline"></h5>
</summary>
<ul class="ccp-execution-list list-group" style="list-style:none">
<li class="ccp-execution-item list-group-item-secondary my-2 p-2" draggable="true">
<li class="ccp-execution-item ccp-archived-execution-item list-group-item-secondary my-2 p-2" draggable="true">
<details>
<summary>
<span name="version" class="badge badge-primary"></span>
<span name="status" class="ml-1 badge"></span>
<div class="d-flex float-right" style="gap: 3px 5px; max-width: 40%; min-width:60px; flex-wrap:wrap;">
${ this.#archive ? `
<button data-index="0" name="restore" title="${this.getLabel("restore_execution_help")}" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 -960 960 960" width="24px" fill="#5f6368">
<path d="M480-250q78 0 134-56t56-134q0-78-56-134t-134-56q-38 0-71 14t-59 38v-62h-60v170h170v-60h-72q17-18 41-29t51-11q54 0 92 38t38 92q0 54-38 92t-92 38q-44 0-77-25.5T356-400h-62q14 65 65.5 107.5T480-250ZM240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T720-80H240Zm0-80h480v-446L526-800H240v640Zm0 0v-640 640Z"/>
</svg>
</button>`
: ``
}
<div class="d-flex float-right" style="gap: 3px 5px;flex-wrap:wrap;">
<button name="restore" title="${this.getLabel("restore_execution_help")}" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 -960 960 960" width="24px" fill="#5f6368">
<path d="M480-250q78 0 134-56t56-134q0-78-56-134t-134-56q-38 0-71 14t-59 38v-62h-60v170h170v-60h-72q17-18 41-29t51-11q54 0 92 38t38 92q0 54-38 92t-92 38q-44 0-77-25.5T356-400h-62q14 65 65.5 107.5T480-250ZM240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T720-80H240Zm0-80h480v-446L526-800H240v640Zm0 0v-640 640Z"/>
</svg>
</button>
<button name="delete" title="${this.getLabel("delete_help")}" class="btn btn-danger ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 0 24 24">
<path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"></path>
</svg>
</button>
</div>
<p name="createstart" class="font-weight-light font-italic" style="margin-top:revert">
<span name="created"></span>.
@ -220,31 +233,8 @@ class CCPExecutionHistory extends HTMLElement {
</p>
</summary>
<div class="d-flex flex-wrap" style="gap:3px; overflow:hidden">
<span style="text-overflow:ellipsis" class="badge badge-light text-info border border-info" name="infrastructure" alt="Infrastructure" title="Infrastructure"></span>
<span class="badge" name="runtime" alt="Runtime" title="Runtime"></span>
</div>
<div class="d-flex flex-column align-items-end" style="gap: 3px;">
<div class="d-flex align-items-center" style="gap:5px" title="${this.getLabel("generate_code_help")}">
<label style="text-wrap:nowrap">${this.getLabel("generate_code")}</label>
<select name="language-selector" class="form-control">
<option value="text/python" data-ext="py" title="Generate plain Python3">Python 3</option>
<option value="text/plain+r" data-ext="r" title="Generate plain R">R</option>
<option value="application/vnd.jupyter+python" data-ext="ipynb" title="Generate Jupyter notebook with Python 3 cells">Jupyter Python3</option>
<option value="text/julia" data-ext="jl" title="Generate Julia 1.9.0 code (HTTP 1.10.0, JSON3 1.13.2)">Julia 1.9.0</option>
<option value="application/x-sh+curl" data-ext="sh" title="Generate Bash script (curl)">Bash (curl)</option>
<option value="application/x-sh+wget" data-ext="sh" title="Generate Bash script (wget)">Bash (wget)</option>
</select>
<button data-index="0" name="codegen" title="Generate code" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 96 960 960">
<path d="m384 721 43-43-101-102 101-101-43-43-144 144.5L384 721Zm192 0 145-145-144-144-43 43 101 101-102 102 43 43ZM180 936q-24.75 0-42.375-17.625T120 876V276q0-24.75 17.625-42.375T180 216h205q5-35 32-57.5t63-22.5q36 0 63 22.5t32 57.5h205q24.75 0 42.375 17.625T840 276v600q0 24.75-17.625 42.375T780 936H180Zm0-60h600V276H180v600Zm300-617q14 0 24.5-10.5T515 224q0-14-10.5-24.5T480 189q-14 0-24.5 10.5T445 224q0 14 10.5 24.5T480 259ZM180 876V276v600Z"/>
</svg>
</button>
</div>
<div class="d-flex align-items-middle" style="gap:5px">
<div class="d-flex">
<a title="${this.getLabel("direct_link_help")}" class="text-truncate" name="direct_link_execution" href="${window.location.href}">${this.getLabel("direct_link")}</a>
</div>
</div>
<span style="text-overflow:ellipsis" class="badge badge-light text-info border border-info" name="infrastructure" alt="${this.getLabel("infrastructure_help")}" title="${this.getLabel("infrastructure_help")}"></span>
<span class="badge" name="runtime" alt="${this.getLabel("runtime_help")}" title="${this.getLabel("runtime_help")}"></span>
</div>
</details>
</li>
@ -283,28 +273,38 @@ class CCPExecutionHistory extends HTMLElement {
<div class="mb-3">
<input accept="application/zip" type="text" name="search" class="form-control" placeholder="Search"/>
</div>
<ul id="switch_executions" class="nav nav-tabs">
<li class="nav-item">
<a name="live_executions" class="nav-link active" href="#">${this.getLabel("live_executions")}</a>
</li>
<li class="nav-item">
<a name="archived_executions" class="nav-link" href="#">${this.getLabel("archived_executions")}</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="live_executions">
<ul name="ccp_execution_list"></ul>
</div>
<div class="tab-pane fade" id="archived_executions">
<ul name="ccp_archived_execution_list"></ul>
</div>
</div>
${
this.#archive ? `
<ul id="switch_executions" class="nav nav-tabs">
<li class="nav-item">
<a name="live_executions" class="nav-link active" href="#">${this.getLabel("live_executions")}</a>
</li>
<li class="nav-item">
<a name="archived_executions" class="nav-link" href="#">${this.getLabel("archived_executions")}</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="live_executions">
<ul name="ccp_execution_list"></ul>
</div>
<div class="tab-pane fade" id="archived_executions">
<ul name="ccp_archived_execution_list"></ul>
</div>
</div>
` : `
<ul name="ccp_execution_list"></ul>
`
}
</div>
</div>
`
this.#rootdoc.querySelector("button[name=refresh]").addEventListener("click", ev=>{
this.refreshExecutions()
this.refreshArchivedExecutions()
if(this.isShowingArchived()){
this.refreshArchivedExecutions()
}else{
this.refreshExecutions()
}
})
this.#searchfield = this.#rootdoc.querySelector("input[name=search]")
@ -312,30 +312,32 @@ class CCPExecutionHistory extends HTMLElement {
this.updateList()
})
const linklive = this.#rootdoc.querySelector("a.nav-link[name=live_executions]")
const linkarch = this.#rootdoc.querySelector("a.nav-link[name=archived_executions]")
const tablive = this.#rootdoc.querySelector("div.tab-pane#live_executions")
const tabarch = this.#rootdoc.querySelector("div.tab-pane#archived_executions")
this.#rootdoc.querySelector("#switch_executions").addEventListener("click", ev=>{
const tgt = ev.target
if(tgt == linklive){
linklive.classList.add("active")
linkarch.classList.remove("active")
tablive.classList.add("show")
tablive.classList.add("active")
tabarch.classList.remove("show")
tabarch.classList.remove("active")
}else if(tgt == linkarch){
linklive.classList.remove("active")
linkarch.classList.add("active")
tabarch.classList.add("show")
tabarch.classList.add("active")
tablive.classList.remove("show")
tablive.classList.remove("active")
}
ev.preventDefault();
ev.stopPropagation()
})
if(this.#archive){
const linklive = this.#rootdoc.querySelector("a.nav-link[name=live_executions]")
const linkarch = this.#rootdoc.querySelector("a.nav-link[name=archived_executions]")
const tablive = this.#rootdoc.querySelector("div.tab-pane#live_executions")
const tabarch = this.#rootdoc.querySelector("div.tab-pane#archived_executions")
this.#rootdoc.querySelector("#switch_executions").addEventListener("click", ev=>{
const tgt = ev.target
if(tgt == linklive){
linklive.classList.add("active")
linkarch.classList.remove("active")
tablive.classList.add("show")
tablive.classList.add("active")
tabarch.classList.remove("show")
tabarch.classList.remove("active")
}else if(tgt == linkarch){
linklive.classList.remove("active")
linkarch.classList.add("active")
tabarch.classList.add("show")
tabarch.classList.add("active")
tablive.classList.remove("show")
tablive.classList.remove("active")
}
ev.preventDefault();
ev.stopPropagation()
})
}
// this.#fileupload = this.#rootdoc.querySelector("label[name=fileupload] > input[type=file]")
// this.#fileupload.addEventListener("change", ev=>{
@ -357,6 +359,10 @@ class CCPExecutionHistory extends HTMLElement {
// })
}
isShowingArchived(){
return this.#archive && this.#rootdoc.querySelector("#archived_executions").classList.contains("show")
}
updateList(){
const filter = this.#searchfield.value
if(filter === "" || filter == null || filter == undefined){
@ -600,6 +606,14 @@ class CCPExecutionHistory extends HTMLElement {
}
}
async deleteArchiveFolder(id){
if(id){
await this.initWorkspace()
await this.#ws.deleteItem(id)
if(this.#archive) await this.refreshArchivedExecutions();
}
}
async initWorkspace(){
if(!this.#ws){
const wsurl = "https://workspace-repository.dev.d4science.org/storagehub/workspace"
@ -618,7 +632,27 @@ class CCPExecutionHistory extends HTMLElement {
}
}
showLoading(el){
el.innerHTML = `
<div class="d-flex justify-content-center" title="${this.getLabel("loading_archived_help")}">
<style>
.spinning {
animation:spin 4s linear infinite;
}
@keyframes spin {
100% {
-webkit-transform: rotate(360deg);
transform:rotate(360deg);
}
}
</style>
<svg class="spinning" style="width:10%" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M160-160v-80h110l-16-14q-52-46-73-105t-21-119q0-111 66.5-197.5T400-790v84q-72 26-116 88.5T240-478q0 45 17 87.5t53 78.5l10 10v-98h80v240H160Zm400-10v-84q72-26 116-88.5T720-482q0-45-17-87.5T650-648l-10-10v98h-80v-240h240v80H690l16 14q49 49 71.5 106.5T800-482q0 111-66.5 197.5T560-170Z"/></svg>
<div>
`
}
async refreshArchivedExecutions(){
this.showLoading(this.#rootdoc.querySelector("ul[name=ccp_archived_execution_list]"))
await this.initWorkspace()
if(this.#execfolderid){
const folders = await this.#ws.listFolder(this.#execfolderid)
@ -629,12 +663,19 @@ class CCPExecutionHistory extends HTMLElement {
for(let j=0; j < executionfolders.length; j++){
const metadatafolder = await this.#ws.findByName(executionfolders[j].id, "metadata")
if(metadatafolder.length === 1){
//parse all metada content that is useful to build up an entry
const metadata = await this.#ws.download(metadatafolder[0].id, null, true)
const zip = new JSZip()
const req = JSON.parse(await (zip.loadAsync(metadata).then(z=>z.file("metadata/request.json").async("string"))))
const status = JSON.parse(await (zip.loadAsync(metadata).then(z=>z.file("metadata/jobStatus.json").async("string"))))
const meth = JSON.parse(await (zip.loadAsync(metadata).then(z=>z.file("metadata/method.json").async("string"))))
const infra = JSON.parse(await (zip.loadAsync(metadata).then(z=>z.file("metadata/infrastructure.json").async("string"))))
const parser = new DOMParser()
// Fasted way to get context is from prov-o. If it doesn't match the context in boot skip this execution
const provo = parser.parseFromString(await zip.loadAsync(metadata).then(z=>z.file("metadata/prov-o.xml").async("string")), "text/xml")
const context = provo.querySelector("entity[*|id='d4s:VRE']>value").textContent
if(context !== this.#boot.context && context.replaceAll("/", "%2F") !== this.#boot.context) continue;
//build entry from downloaded data
const entry = {
id : req.id,
status : status.status,
@ -647,9 +688,10 @@ class CCPExecutionHistory extends HTMLElement {
infrastructure: infra.name,
infrastructuretype : infra.type,
ccpnote : req.inputs.ccpnote ? req.inputs.ccpnote : "",
runtime : req.inputs.ccpruntime,
runtime : req.inputs.ccpimage,
replicas : req.inputs.ccpreplicas ? req.inputs.ccpreplicas : 1,
href : `items/${executionfolders[j].id}/download`
href : `items/${executionfolders[j].id}/download`,
wsid : executionfolders[j].id
}
if(this.#archived[entry.method]) this.#archived[entry.method].push(entry);
else this.#archived[entry.method] = [entry]
@ -856,7 +898,8 @@ class CCPExecutionHistory extends HTMLElement {
e.textContent = rt + (d.replicas && d.replicas !== "1" ? ' x ' + d.replicas : '')
const t = infratype.match(/docker/i) ? "docker" : null
const t2 = !t && infratype.match(/lxd/i) ? "lxd" : t
e.classList.add(t2)
const t3 = !t2 && infratype.match(/galaxy/i) ? "galaxy" : t
e.classList.add(t3)
}
},
{
@ -909,7 +952,7 @@ class CCPExecutionHistory extends HTMLElement {
}
#archived_execution_list_bss = {
template : "#ARCHIVED_EXECUTIOM_LIST_TEMPLATE",
template : "#ARCHIVED_EXECUTION_LIST_TEMPLATE",
target : "ul[name=ccp_archived_execution_list]",
in : ()=>this,
recurse:[
@ -921,14 +964,14 @@ class CCPExecutionHistory extends HTMLElement {
target : "details[name=level1]",
apply : (e,d)=>{
e.alt = e.title = d
if(sessionStorage.getItem(d) === "open") e.open = "open";
if(sessionStorage.getItem("arch_" + d) === "open") e.open = "open";
else e.removeAttribute("open");
},
on_toggle : ev=>{
if(ev.target.open){
sessionStorage.setItem(ev.currentTarget.alt, 'open')
sessionStorage.setItem("arch_" + ev.currentTarget.alt, 'open')
}else{
sessionStorage.removeItem(ev.currentTarget.alt)
sessionStorage.removeItem("arch_" + ev.currentTarget.alt)
}
},
},
@ -942,6 +985,7 @@ class CCPExecutionHistory extends HTMLElement {
apply : (e,d)=>{
e.setAttribute("data-index", d.id)
e.setAttribute("data-href", d.href)
e.setAttribute("data-wsid", d.wsid)
},
on_click: ev=>{
if(ev.target.getAttribute("name") === "restore"){
@ -949,21 +993,27 @@ class CCPExecutionHistory extends HTMLElement {
const href = ev.currentTarget.getAttribute("data-href")
this.fromArchiveFolder(href)
}
}else if(ev.target.getAttribute("name") === "delete"){
if(window.confirm(this.getLabel("confirm_delete_archived_execution"))){
const wsid = ev.currentTarget.getAttribute("data-wsid")
this.deleteArchiveFolder(wsid)
}
}
},
recurse : [
{
target : "details",
apply : (e,d)=>{
e.alt = e.title = d.id
if(sessionStorage.getItem(d.id) === "open") e.open = "open";
if(sessionStorage.getItem("arch_" + d.id) === "open") e.open = "open";
else e.removeAttribute("open");
},
on_toggle : ev=>{
if(ev.target.open){
sessionStorage.setItem(ev.currentTarget.alt, 'open')
sessionStorage.setItem("arch_" + ev.currentTarget.alt, 'open')
}else{
sessionStorage.removeItem(ev.currentTarget.alt)
sessionStorage.removeItem("arch_" + ev.currentTarget.alt)
}
}
},
@ -1037,7 +1087,8 @@ class CCPExecutionHistory extends HTMLElement {
e.textContent = rt + (d.replicas && d.replicas !== "1" ? ' x ' + d.replicas : '')
const t = infratype.match(/docker/i) ? "docker" : null
const t2 = !t && infratype.match(/lxd/i) ? "lxd" : t
e.classList.add(t2)
const t3 = !t2 && infratype.match(/galaxy/i) ? "galaxy" : t
e.classList.add(t3)
}
},
{