qianmo_job

self mode

当前为 2019-10-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         qianmo_job
// @version      0.0.3
// @include      http://newqianmo.baidu.com/404
// @description  self mode
// @run-at       document-start
// @namespace    https://greasyfork.org/users/164996
// ==/UserScript==
const head = `<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>job</title>
<style>
  body {
    user-select: none;
    cursor: pointer;
    margin: 0;
  }
  #table {
    display: grid;
    grid-template-columns: repeat(9, auto);
    grid-column-gap: 1em;
  }
  #node {
    #width: fit-content;
    display: grid;
    grid-template-columns: repeat(6, auto);
    grid-column-gap: 1em;
    color: #9E9E9E;
  }
  div.running {
    color: #2196F3;
  }
  div.other {
    color: #9E9E9E;
  }
  div.cancelled {
    color: #FF5722;
  }
  div.failed {
    color: #E91E63;
  }
  div.completed {
    color: #673AB7;
  }
  div.pending {
    color: #FF9800;
  }
  div.preempted {
    color: #3f51b5;
  }
  #disconnect{
    display: none;
    color: #bbb;
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
  }
  span.button{
    user-select:none;
    cursor:pointer;
  }
  div.section{
    grid-column: 1 / -1;
  }
  iframe{
    display: none;
  }
</style>`
const body = `<div id="app">
<div id='disconnect'>disconnect</div>
<div id="node"></div>
<div id="table"></div>
<iframe src="http://newqianmo.baidu.com/index.jsp#/console"></iframe>
</div>`
document.head.innerHTML = head
document.body.innerHTML = body
const timeout = 10000
const app = document.querySelector('#app')
const node = document.querySelector('#node')
const table = document.querySelector('#table')
const disconnect = document.querySelector('#disconnect')
const copy = text => {
  const textArea = document.createElement('textarea')
  textArea.value = text
  document.body.appendChild(textArea)
  textArea.select()
  document.execCommand('copy')
  document.body.removeChild(textArea)
}
const parse = s => {
  const t = document.implementation.createHTMLDocument()
  t.body.innerHTML = s
  return t
}
const fetchWithTimeout = (url, options) => {
  return Promise.race([
    fetch(url, options),
    new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
  ])
}
const fetchList = async url => {
  let data = await fetchWithTimeout(url)
  data = await data.json()
  data = data.retData.rows
  return data
}
const fetchResource = async () => {
  const url =
    'http://newqianmo.baidu.com/action/gpu/queryGpuResource?pageNum=1&pageSize=5&isPersonal=1'
  return await fetchList(url)
}
const fetchTask = async (type = '', size = 20) => {
  const url = `http://newqianmo.baidu.com/action/gpu/queryGpuJobListPerson?pageNum=1&pageSize=${size}&isProjectList=0&jobId=&jobName=&queueName=&status=`
  return await fetchList(url + type)
}
const main = async () => {
  let data
  try {
    data = (await Promise.all([
      fetchTask('RUNNING'),
      fetchTask('PENDING', 5),
      fetchTask('FAILED', 5),
      fetchTask('PREEMPTED', 5),
      fetchTask('CANCELLED', 5)
      //fetchTask('COMPLETED', 5),
    ]))
      .filter(i => i)
      .flat()
  } catch (err) {
    disconnect.style.display = 'block'
    return
  }
  disconnect.style.display = 'none'
  let type_pre = ''
  let b = data.reduce((a, c) => {
    let url = ''
    try {
      url = new URL(c.jobLogUrl)
      url.pathname = ''
      url.port = 8825
    } catch {}
    const link = `<a target="_blank" href="${url}">tb</a>
      <a target="_blank" href="${c.jobLogUrl}">job</a>
      <a target="_blank" href="${c.workspaceUrl}">workspace</a>`
    url = url.hostname
    const ssh_action = `ssh slurm@${url} -t 'cd job/tmp/job-${c.slurmId};bash'`
    const rsync_action = `rsync -av slurm@${url}:job/tmp/job-${c.slurmId}/cdsr/runs .`
    const action = `<span class=button data-copy="${ssh_action}">ssh</span>
    <span class=button  data-copy="${rsync_action}">rsync</span>
    <span class=button data-copy="deljob -j${c.jobId}">deljob</span>`
    const list = [
      c.jobName,
      c.jobId,
      c.priority,
      c.queueName,
      c.ncpus,
      c.gpuRatio,
      c.elapsed,
      action,
      link
    ]
    const type = c.status.toLowerCase()
    if (type !== type_pre) {
      type_pre = type
      a += `<div class="section ${type}">${type}</div>`
    }
    const row = list.map(i => `<div class=${type}>` + i + '</div>').join('')
    return a + row
  }, '')
  // node
  data = await fetchResource()
  let d = data.reduce((a, c) => {
    const list = [
      c.queueName,
      c.gpuType + ' x ' + c.gpuPerNode,
      Number(c.quota) - Number(c.used)
    ]
    const row = list.map(i => `<div>` + i + '</div>').join('')
    return a + row
  }, '')
  requestAnimationFrame(() => {
    node.innerHTML = d
    table.innerHTML = b
  })
}
main()
let timer = setInterval(main, timeout)
let changeTimer
document.addEventListener('click', e => {
  const type = e.target.textContent
  const target = e.target
  let text = ''
  if (['ssh', 'rsync', 'deljob'].includes(type)) {
    text = target.dataset.copy
  } else if (target.children.length === 0) {
    text = target.textContent.trim()
  }
  if (text.length === 0) return
  copy(text)
})
document.addEventListener('visibilitychange', () => {
  clearTimeout(changeTimer)
  if (document.hidden) {
    changeTimer = setTimeout(() => clearInterval(timer), timeout)
  } else {
    main()
    clearInterval(timer)
    timer = setInterval(main, timeout)
  }
})

const frame = document.querySelector('iframe')
setInterval(() => {
  frame.contentWindow.location.reload()
}, 1000 * 3600)