mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-06-08 08:33:49 +02:00
Add sorting to compareTable
This commit is contained in:
parent
f210a5f508
commit
518cb34340
@ -2,7 +2,6 @@
|
|||||||
@component jobCompare component; compares jobs according to set filters or job selection
|
@component jobCompare component; compares jobs according to set filters or job selection
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
- `sorting Object?`: Currently active sorting [Default: {field: "startTime", type: "col", order: "DESC"}]
|
|
||||||
- `matchedJobs Number?`: Number of matched jobs for selected filters [Default: 0]
|
- `matchedJobs Number?`: Number of matched jobs for selected filters [Default: 0]
|
||||||
- `metrics [String]?`: The currently selected metrics [Default: User-Configured Selection]
|
- `metrics [String]?`: The currently selected metrics [Default: User-Configured Selection]
|
||||||
- `showFootprint Bool`: If to display the jobFootprint component
|
- `showFootprint Bool`: If to display the jobFootprint component
|
||||||
@ -21,7 +20,7 @@
|
|||||||
// mutationStore,
|
// mutationStore,
|
||||||
} from "@urql/svelte";
|
} from "@urql/svelte";
|
||||||
import { Row, Col, Card, Spinner, Table, Input, InputGroup, InputGroupText, Icon } from "@sveltestrap/sveltestrap";
|
import { Row, Col, Card, Spinner, Table, Input, InputGroup, InputGroupText, Icon } from "@sveltestrap/sveltestrap";
|
||||||
import { formatTime } from "./units.js";
|
import { formatTime, roundTwoDigits } from "./units.js";
|
||||||
import Comparogram from "./plots/Comparogram.svelte";
|
import Comparogram from "./plots/Comparogram.svelte";
|
||||||
|
|
||||||
const ccconfig = getContext("cc-config"),
|
const ccconfig = getContext("cc-config"),
|
||||||
@ -34,6 +33,8 @@
|
|||||||
|
|
||||||
let filter = [...filterBuffer] || [];
|
let filter = [...filterBuffer] || [];
|
||||||
let comparePlotData = {};
|
let comparePlotData = {};
|
||||||
|
let compareTableData = [];
|
||||||
|
let compareTableSorting = {};
|
||||||
let jobIds = [];
|
let jobIds = [];
|
||||||
let jobClusters = [];
|
let jobClusters = [];
|
||||||
let tableJobIDFilter = "";
|
let tableJobIDFilter = "";
|
||||||
@ -82,9 +83,33 @@
|
|||||||
jobIds = [];
|
jobIds = [];
|
||||||
jobClusters = [];
|
jobClusters = [];
|
||||||
comparePlotData = {};
|
comparePlotData = {};
|
||||||
|
compareTableData = [...$compareData.data.jobsMetricStats];
|
||||||
jobs2uplot($compareData.data.jobsMetricStats, metrics);
|
jobs2uplot($compareData.data.jobsMetricStats, metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: if ((!$compareData.fetching && !$compareData.error) && metrics) {
|
||||||
|
// Meta
|
||||||
|
compareTableSorting['meta'] = {
|
||||||
|
startTime: { dir: "down", active: true },
|
||||||
|
duration: { dir: "up", active: false },
|
||||||
|
cluster: { dir: "up", active: false },
|
||||||
|
};
|
||||||
|
// Resources
|
||||||
|
compareTableSorting['resources'] = {
|
||||||
|
Nodes: { dir: "up", active: false },
|
||||||
|
Threads: { dir: "up", active: false },
|
||||||
|
Accs: { dir: "up", active: false },
|
||||||
|
};
|
||||||
|
// Metrics
|
||||||
|
for (let metric of metrics) {
|
||||||
|
compareTableSorting[metric] = {
|
||||||
|
min: { dir: "up", active: false },
|
||||||
|
avg: { dir: "up", active: false },
|
||||||
|
max: { dir: "up", active: false },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* FUNCTIONS */
|
/* FUNCTIONS */
|
||||||
// (Re-)query and optionally set new filters; Query will be started reactively.
|
// (Re-)query and optionally set new filters; Query will be started reactively.
|
||||||
export function queryJobs(filters) {
|
export function queryJobs(filters) {
|
||||||
@ -97,6 +122,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sortBy(key, field) {
|
||||||
|
let s = compareTableSorting[key][field];
|
||||||
|
if (s.active) {
|
||||||
|
s.dir = s.dir == "up" ? "down" : "up";
|
||||||
|
} else {
|
||||||
|
for (let key in compareTableSorting)
|
||||||
|
for (let field in compareTableSorting[key]) compareTableSorting[key][field].active = false;
|
||||||
|
s.active = true;
|
||||||
|
}
|
||||||
|
compareTableSorting = { ...compareTableSorting };
|
||||||
|
|
||||||
|
if (key == 'resources') {
|
||||||
|
let longField = "";
|
||||||
|
switch (field) {
|
||||||
|
case "Nodes":
|
||||||
|
longField = "numNodes"
|
||||||
|
break
|
||||||
|
case "Threads":
|
||||||
|
longField = "numHWThreads"
|
||||||
|
break
|
||||||
|
case "Accs":
|
||||||
|
longField = "numAccelerators"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.log("Unknown Res Field", field)
|
||||||
|
}
|
||||||
|
compareTableData = compareTableData.sort((j1, j2) => {
|
||||||
|
if (j1[longField] == null || j2[longField] == null) return -1;
|
||||||
|
return s.dir != "up" ? j1[longField] - j2[longField] : j2[longField] - j1[longField];
|
||||||
|
});
|
||||||
|
} else if (key == 'meta') {
|
||||||
|
compareTableData = compareTableData.sort((j1, j2) => {
|
||||||
|
if (j1[field] == null || j2[field] == null) return -1;
|
||||||
|
if (field == 'cluster') {
|
||||||
|
let c1 = `${j1.cluster} (${j1.subCluster})`
|
||||||
|
let c2 = `${j2.cluster} (${j2.subCluster})`
|
||||||
|
return s.dir != "up" ? c1.localeCompare(c2) : c2.localeCompare(c1)
|
||||||
|
} else {
|
||||||
|
return s.dir != "up" ? j1[field] - j2[field] : j2[field] - j1[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
compareTableData = compareTableData.sort((j1, j2) => {
|
||||||
|
let s1 = j1.stats.find((m) => m.name == key)?.data;
|
||||||
|
let s2 = j2.stats.find((m) => m.name == key)?.data;
|
||||||
|
if (s1 == null || s2 == null) return -1;
|
||||||
|
return s.dir != "up" ? s1[field] - s2[field] : s2[field] - s1[field];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function jobs2uplot(jobs, metrics) {
|
function jobs2uplot(jobs, metrics) {
|
||||||
// Resources Init
|
// Resources Init
|
||||||
comparePlotData['resources'] = {unit:'', data: [[],[],[],[],[],[]]} // data: [X, XST, XRT, YNODES, YTHREADS, YACCS]
|
comparePlotData['resources'] = {unit:'', data: [[],[],[],[],[],[]]} // data: [X, XST, XRT, YNODES, YTHREADS, YACCS]
|
||||||
@ -219,11 +295,10 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<!-- Header Row 1 -->
|
<!-- Header Row 1 -->
|
||||||
<tr>
|
<tr>
|
||||||
<th>Index</th>
|
<th style="width:8%; max-width:10%;">JobID</th>
|
||||||
<th style="width:10%">JobID</th>
|
|
||||||
<th>Cluster</th>
|
|
||||||
<th>StartTime</th>
|
<th>StartTime</th>
|
||||||
<th>Duration</th>
|
<th>Duration</th>
|
||||||
|
<th>Cluster</th>
|
||||||
<th colspan="3">Resources</th>
|
<th colspan="3">Resources</th>
|
||||||
{#each metrics as metric}
|
{#each metrics as metric}
|
||||||
<th colspan="3">{metric}</th>
|
<th colspan="3">{metric}</th>
|
||||||
@ -231,47 +306,87 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<!-- Header Row 2: Fields -->
|
<!-- Header Row 2: Fields -->
|
||||||
<tr>
|
<tr>
|
||||||
<th/>
|
<th>
|
||||||
<th style="width:10%">
|
<InputGroup size="sm">
|
||||||
<InputGroup>
|
<Input type="text" bind:value={tableJobIDFilter}/>
|
||||||
<InputGroupText>
|
<InputGroupText>
|
||||||
<Icon name="search"></Icon>
|
<Icon name="search"></Icon>
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
<Input type="text" bind:value={tableJobIDFilter}/>
|
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</th>
|
</th>
|
||||||
<th/>
|
<th on:click={() => sortBy('meta', 'startTime')}>
|
||||||
<th/>
|
Sort
|
||||||
<th/>
|
<Icon
|
||||||
|
name="caret-{compareTableSorting['meta']['startTime'].dir}{compareTableSorting['meta']['startTime']
|
||||||
|
.active
|
||||||
|
? '-fill'
|
||||||
|
: ''}"
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th on:click={() => sortBy('meta', 'duration')}>
|
||||||
|
Sort
|
||||||
|
<Icon
|
||||||
|
name="caret-{compareTableSorting['meta']['duration'].dir}{compareTableSorting['meta']['duration']
|
||||||
|
.active
|
||||||
|
? '-fill'
|
||||||
|
: ''}"
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th on:click={() => sortBy('meta', 'cluster')}>
|
||||||
|
Sort
|
||||||
|
<Icon
|
||||||
|
name="caret-{compareTableSorting['meta']['cluster'].dir}{compareTableSorting['meta']['cluster']
|
||||||
|
.active
|
||||||
|
? '-fill'
|
||||||
|
: ''}"
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
{#each ["Nodes", "Threads", "Accs"] as res}
|
{#each ["Nodes", "Threads", "Accs"] as res}
|
||||||
<th>{res}</th>
|
<th on:click={() => sortBy('resources', res)}>
|
||||||
|
{res}
|
||||||
|
<Icon
|
||||||
|
name="caret-{compareTableSorting['resources'][res].dir}{compareTableSorting['resources'][res]
|
||||||
|
.active
|
||||||
|
? '-fill'
|
||||||
|
: ''}"
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
{/each}
|
{/each}
|
||||||
{#each metrics as metric}
|
{#each metrics as metric}
|
||||||
{#each ["min", "avg", "max"] as stat}
|
{#each ["min", "avg", "max"] as stat}
|
||||||
<th>{stat}</th>
|
<th on:click={() => sortBy(metric, stat)}>
|
||||||
|
{stat}
|
||||||
|
<Icon
|
||||||
|
name="caret-{compareTableSorting[metric][stat].dir}{compareTableSorting[metric][stat]
|
||||||
|
.active
|
||||||
|
? '-fill'
|
||||||
|
: ''}"
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each $compareData.data.jobsMetricStats.filter((j) => j.jobId.includes(tableJobIDFilter)) as job, jindex (job.jobId)}
|
{#each compareTableData.filter((j) => j.jobId.includes(tableJobIDFilter)) as job (job.id)}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{jindex}</td>
|
<td><b><a href="/monitoring/job/{job.id}" target="_blank">{job.jobId}</a></b></td>
|
||||||
<td><a href="/monitoring/job/{job.id}" target="_blank">{job.jobId}</a></td>
|
<td>{new Date(job.startTime * 1000).toLocaleString()}</td>
|
||||||
<td>{job.cluster} ({job.subCluster})</td>
|
|
||||||
<td>{new Date(job.startTime * 1000).toISOString()}</td>
|
|
||||||
<td>{formatTime(job.duration)}</td>
|
<td>{formatTime(job.duration)}</td>
|
||||||
|
<td>{job.cluster} ({job.subCluster})</td>
|
||||||
<td>{job.numNodes}</td>
|
<td>{job.numNodes}</td>
|
||||||
<td>{job.numHWThreads}</td>
|
<td>{job.numHWThreads}</td>
|
||||||
<td>{job.numAccelerators}</td>
|
<td>{job.numAccelerators}</td>
|
||||||
{#each metrics as metric}
|
{#each metrics as metric}
|
||||||
<td>{job.stats.find((s) => s.name == metric).data.min}</td>
|
<td>{roundTwoDigits(job.stats.find((s) => s.name == metric).data.min)}</td>
|
||||||
<td>{job.stats.find((s) => s.name == metric).data.avg}</td>
|
<td>{roundTwoDigits(job.stats.find((s) => s.name == metric).data.avg)}</td>
|
||||||
<td>{job.stats.find((s) => s.name == metric).data.max}</td>
|
<td>{roundTwoDigits(job.stats.find((s) => s.name == metric).data.max)}</td>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
{:else}
|
{:else}
|
||||||
<tr> No jobs found </tr>
|
<tr>
|
||||||
|
<td colspan={7 + (metrics.length * 3)}><b>No jobs found.</b></td>
|
||||||
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user