master #18
|
@ -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">
|
||||
<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>
|
||||
<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,6 +273,8 @@ class CCPExecutionHistory extends HTMLElement {
|
|||
<div class="mb-3">
|
||||
<input accept="application/zip" type="text" name="search" class="form-control" placeholder="Search"/>
|
||||
</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>
|
||||
|
@ -299,12 +291,20 @@ class CCPExecutionHistory extends HTMLElement {
|
|||
<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()
|
||||
if(this.isShowingArchived()){
|
||||
this.refreshArchivedExecutions()
|
||||
}else{
|
||||
this.refreshExecutions()
|
||||
}
|
||||
})
|
||||
|
||||
this.#searchfield = this.#rootdoc.querySelector("input[name=search]")
|
||||
|
@ -312,6 +312,7 @@ class CCPExecutionHistory extends HTMLElement {
|
|||
this.updateList()
|
||||
})
|
||||
|
||||
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")
|
||||
|
@ -336,6 +337,7 @@ class CCPExecutionHistory extends HTMLElement {
|
|||
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)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue