File size: 2,755 Bytes
2a49d9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{% extends "base.html" %}
{% set nav = 'models' %}
{% block title %}Models — NanoDex{% endblock %}
{% block body %}

<div class="sect-h" style="margin-bottom:22px">
  <div>
    <span class="eyebrow">Your models</span>
    <h1 style="font-size:28px;margin-top:5px">Everything you've trained</h1>
  </div>
  <a class="btn primary" href="/train">+ New run</a>
</div>

<div style="display:flex;gap:7px;flex-wrap:wrap;margin-bottom:22px" id="filters">
  <button class="btn sm on" data-f="all">All</button>
  <button class="btn sm" data-f="running">Training</button>
  <button class="btn sm" data-f="queued">Queued</button>
  <button class="btn sm" data-f="done">Completed</button>
  <button class="btn sm" data-f="other">Failed / cancelled</button>
</div>

<section id="sec-running" style="margin-bottom:32px"></section>
<section id="sec-queued"  style="margin-bottom:32px"></section>
<section id="sec-done"    style="margin-bottom:32px"></section>
<section id="sec-other"></section>
<div id="none"></div>

{% endblock %}
{% block scripts %}
<script>
let filter = 'all';

document.querySelectorAll('#filters .btn').forEach(b =>
  b.addEventListener('click', () => {
    document.querySelectorAll('#filters .btn').forEach(x =>
      x.classList.remove('primary'));
    b.classList.add('primary');
    filter = b.dataset.f;
    render(last);
  }));
document.querySelector('#filters .btn').classList.add('primary');

let last = [];

function section(id, title, note, jobs){
  const el = document.getElementById(id);
  if(!jobs.length){ el.innerHTML = ''; return 0; }
  el.innerHTML = `<div class="sect-h">
      <div><h2>${title} <span class="muted" style="font-weight:400">
        (${jobs.length})</span></h2>
        ${note ? `<p class="sub" style="margin-top:3px">${note}</p>` : ''}</div>
    </div>
    <div class="runs">${jobs.map(NX.runRow).join('')}</div>`;
  return jobs.length;
}

function render(jobs){
  last = jobs;
  const pick = s => (filter==='all' || filter===s) ? jobs.filter(j =>
    s==='other' ? (j.status==='failed'||j.status==='cancelled') : j.status===s) : [];

  let n = 0;
  n += section('sec-running','Training now','Live on a worker right now.', pick('running'));
  n += section('sec-queued','In the queue','Waiting for a free worker — oldest first.', pick('queued'));
  n += section('sec-done','Completed','Finished. Open one to publish it or generate text.', pick('done'));
  n += section('sec-other','Failed or cancelled','', pick('other'));

  document.getElementById('none').innerHTML = n ? '' :
    `<div class="empty">Nothing here yet.
       <a href="/train" style="color:var(--accent)">Train your first model →</a></div>`;
}

NX.poll(async () => render((await NX.get('/api/jobs')).jobs), 4000);
</script>
{% endblock %}