2022-06-22 11:20:57 +02:00
|
|
|
<!--
|
|
|
|
@component
|
|
|
|
|
|
|
|
Properties:
|
|
|
|
- metrics: [String] (can change from outside)
|
|
|
|
- sorting: { field: String, order: "DESC" | "ASC" } (can change from outside)
|
|
|
|
- matchedJobs: Number (changes from inside)
|
|
|
|
Functions:
|
|
|
|
- update(filters?: [JobFilter])
|
|
|
|
-->
|
|
|
|
<script>
|
2023-05-05 10:07:12 +02:00
|
|
|
import {
|
|
|
|
queryStore,
|
|
|
|
gql,
|
|
|
|
getContextClient,
|
|
|
|
mutationStore,
|
|
|
|
} from "@urql/svelte";
|
|
|
|
import { getContext } from "svelte";
|
|
|
|
import { Row, Table, Card, Spinner } from "sveltestrap";
|
|
|
|
import Pagination from "./Pagination.svelte";
|
|
|
|
import JobListRow from "./Row.svelte";
|
|
|
|
import { stickyHeader } from "../utils.js";
|
|
|
|
|
|
|
|
const ccconfig = getContext("cc-config"),
|
|
|
|
clusters = getContext("clusters"),
|
|
|
|
initialized = getContext("initialized");
|
|
|
|
|
|
|
|
export let sorting = { field: "startTime", order: "DESC" };
|
|
|
|
export let matchedJobs = 0;
|
|
|
|
export let metrics = ccconfig.plot_list_selectedMetrics;
|
|
|
|
|
|
|
|
let itemsPerPage = ccconfig.plot_list_jobsPerPage;
|
|
|
|
let page = 1;
|
|
|
|
let paging = { itemsPerPage, page };
|
|
|
|
let filter = [];
|
|
|
|
|
2023-05-08 18:06:36 +02:00
|
|
|
const client = getContextClient();
|
|
|
|
const query = gql`
|
|
|
|
query (
|
|
|
|
$filter: [JobFilter!]!
|
|
|
|
$sorting: OrderByInput!
|
|
|
|
$paging: PageRequest!
|
|
|
|
) {
|
|
|
|
jobs(filter: $filter, order: $sorting, page: $paging) {
|
|
|
|
items {
|
|
|
|
id
|
|
|
|
jobId
|
|
|
|
user
|
|
|
|
project
|
|
|
|
jobName
|
|
|
|
cluster
|
|
|
|
subCluster
|
|
|
|
startTime
|
|
|
|
duration
|
|
|
|
numNodes
|
|
|
|
numHWThreads
|
|
|
|
numAcc
|
|
|
|
walltime
|
|
|
|
resources {
|
|
|
|
hostname
|
|
|
|
}
|
|
|
|
SMT
|
|
|
|
exclusive
|
|
|
|
partition
|
|
|
|
arrayJobId
|
|
|
|
monitoringStatus
|
|
|
|
state
|
|
|
|
tags {
|
2023-05-05 10:07:12 +02:00
|
|
|
id
|
2023-05-08 18:06:36 +02:00
|
|
|
type
|
|
|
|
name
|
|
|
|
}
|
|
|
|
userData {
|
|
|
|
name
|
2023-05-05 10:07:12 +02:00
|
|
|
}
|
2023-05-08 18:06:36 +02:00
|
|
|
metaData
|
2023-05-05 10:07:12 +02:00
|
|
|
}
|
2023-05-08 18:06:36 +02:00
|
|
|
count
|
2022-06-22 11:20:57 +02:00
|
|
|
}
|
2023-05-08 18:06:36 +02:00
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
$: jobs = queryStore({
|
2023-05-12 11:05:39 +02:00
|
|
|
client: client,
|
|
|
|
query: query,
|
|
|
|
variables: { paging, sorting, filter }
|
2023-05-05 10:07:12 +02:00
|
|
|
});
|
2022-06-22 11:20:57 +02:00
|
|
|
|
2023-05-12 11:05:39 +02:00
|
|
|
$: matchedJobs = $jobs.data != null ? $jobs.data.jobs.count : 0;
|
2023-05-05 17:01:50 +02:00
|
|
|
|
2023-05-12 11:05:39 +02:00
|
|
|
// Force refresh list with existing unchanged variables (== usually would not trigger reactivity)
|
|
|
|
export function refresh() {
|
|
|
|
queryStore({
|
|
|
|
client: client,
|
|
|
|
query: query,
|
|
|
|
variables: { paging, sorting, filter },
|
|
|
|
requestPolicy: 'network-only'
|
2023-05-05 10:07:12 +02:00
|
|
|
});
|
2023-05-05 17:01:50 +02:00
|
|
|
}
|
|
|
|
|
2022-06-22 11:20:57 +02:00
|
|
|
// (Re-)query and optionally set new filters.
|
|
|
|
export function update(filters) {
|
|
|
|
if (filters != null) {
|
2023-05-05 10:07:12 +02:00
|
|
|
let minRunningFor = ccconfig.plot_list_hideShortRunningJobs;
|
2022-06-22 11:20:57 +02:00
|
|
|
if (minRunningFor && minRunningFor > 0) {
|
2023-05-05 10:07:12 +02:00
|
|
|
filters.push({ minRunningFor });
|
2022-06-22 11:20:57 +02:00
|
|
|
}
|
2023-05-05 10:07:12 +02:00
|
|
|
filter = filters;
|
2022-06-22 11:20:57 +02:00
|
|
|
}
|
2023-05-05 10:07:12 +02:00
|
|
|
page = 1;
|
|
|
|
paging = paging = { page, itemsPerPage };
|
2022-06-22 11:20:57 +02:00
|
|
|
}
|
|
|
|
|
2023-05-12 11:05:39 +02:00
|
|
|
const updateConfigurationMutation = ({ name, value }) => {
|
|
|
|
return mutationStore({
|
|
|
|
client: client,
|
|
|
|
query: gql`
|
|
|
|
mutation ($name: String!, $value: String!) {
|
|
|
|
updateConfiguration(name: $name, value: $value)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
variables: { name, value }
|
|
|
|
});
|
2023-05-08 18:06:36 +02:00
|
|
|
}
|
|
|
|
|
2023-05-12 11:05:39 +02:00
|
|
|
function updateConfiguration(value, page) {
|
|
|
|
updateConfigurationMutation({ name: 'plot_list_jobsPerPage', value: value })
|
|
|
|
.subscribe(res => {
|
|
|
|
if (res.fetching === false && !res.error) {
|
|
|
|
paging = { itemsPerPage: value, page: page }; // Trigger reload of jobList
|
|
|
|
} else if (res.fetching === false && res.error) {
|
|
|
|
throw res.error
|
|
|
|
// console.log('Error on subscription: ' + res.error)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
2023-05-05 10:07:12 +02:00
|
|
|
let tableWidth = null;
|
|
|
|
let jobInfoColumnWidth = 250;
|
2023-05-12 11:05:39 +02:00
|
|
|
|
2023-05-05 10:07:12 +02:00
|
|
|
$: plotWidth = Math.floor(
|
|
|
|
(tableWidth - jobInfoColumnWidth) / metrics.length - 10
|
|
|
|
);
|
2022-06-22 11:20:57 +02:00
|
|
|
|
2023-05-05 10:07:12 +02:00
|
|
|
let headerPaddingTop = 0;
|
|
|
|
stickyHeader(
|
|
|
|
".cc-table-wrapper > table.table >thead > tr > th.position-sticky:nth-child(1)",
|
|
|
|
(x) => (headerPaddingTop = x)
|
|
|
|
);
|
2022-06-22 11:20:57 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<Row>
|
|
|
|
<div class="col cc-table-wrapper" bind:clientWidth={tableWidth}>
|
|
|
|
<Table cellspacing="0px" cellpadding="0px">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
2023-05-05 10:07:12 +02:00
|
|
|
<th
|
|
|
|
class="position-sticky top-0"
|
|
|
|
scope="col"
|
|
|
|
style="width: {jobInfoColumnWidth}px; padding-top: {headerPaddingTop}px"
|
|
|
|
>
|
2022-06-22 11:20:57 +02:00
|
|
|
Job Info
|
|
|
|
</th>
|
|
|
|
{#each metrics as metric (metric)}
|
2023-05-05 10:07:12 +02:00
|
|
|
<th
|
|
|
|
class="position-sticky top-0 text-center"
|
|
|
|
scope="col"
|
|
|
|
style="width: {plotWidth}px; padding-top: {headerPaddingTop}px"
|
|
|
|
>
|
2022-06-22 11:20:57 +02:00
|
|
|
{metric}
|
|
|
|
{#if $initialized}
|
|
|
|
({clusters
|
2023-05-05 10:07:12 +02:00
|
|
|
.map((cluster) =>
|
|
|
|
cluster.metricConfig.find(
|
|
|
|
(m) => m.name == metric
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.filter((m) => m != null)
|
|
|
|
.map(
|
|
|
|
(m) =>
|
|
|
|
(m.unit?.prefix
|
|
|
|
? m.unit?.prefix
|
|
|
|
: "") +
|
|
|
|
(m.unit?.base ? m.unit?.base : "")
|
|
|
|
) // Build unitStr
|
|
|
|
.reduce(
|
|
|
|
(arr, unitStr) =>
|
|
|
|
arr.includes(unitStr)
|
|
|
|
? arr
|
|
|
|
: [...arr, unitStr],
|
|
|
|
[]
|
|
|
|
) // w/o this, output would be [unitStr, unitStr]
|
|
|
|
.join(", ")})
|
2022-06-22 11:20:57 +02:00
|
|
|
{/if}
|
|
|
|
</th>
|
|
|
|
{/each}
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
{#if $jobs.error}
|
|
|
|
<tr>
|
2023-05-05 10:07:12 +02:00
|
|
|
<td colspan={metrics.length + 1}>
|
|
|
|
<Card body color="danger" class="mb-3"
|
|
|
|
><h2>{$jobs.error.message}</h2></Card
|
|
|
|
>
|
2022-06-22 11:20:57 +02:00
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
{:else if $jobs.fetching || !$jobs.data}
|
|
|
|
<tr>
|
2023-05-05 10:07:12 +02:00
|
|
|
<td colspan={metrics.length + 1}>
|
2022-06-22 11:20:57 +02:00
|
|
|
<Spinner secondary />
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
{:else if $jobs.data && $initialized}
|
|
|
|
{#each $jobs.data.jobs.items as job (job)}
|
2023-05-05 10:07:12 +02:00
|
|
|
<JobListRow {job} {metrics} {plotWidth} />
|
2022-06-22 11:20:57 +02:00
|
|
|
{:else}
|
2023-05-05 10:07:12 +02:00
|
|
|
<tr>
|
|
|
|
<td colspan={metrics.length + 1}>
|
|
|
|
No jobs found
|
|
|
|
</td>
|
|
|
|
</tr>
|
2022-06-22 11:20:57 +02:00
|
|
|
{/each}
|
|
|
|
{/if}
|
|
|
|
</tbody>
|
|
|
|
</Table>
|
|
|
|
</div>
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
<Pagination
|
2023-05-05 10:07:12 +02:00
|
|
|
bind:page
|
2022-06-22 11:20:57 +02:00
|
|
|
{itemsPerPage}
|
|
|
|
itemText="Jobs"
|
|
|
|
totalItems={matchedJobs}
|
|
|
|
on:update={({ detail }) => {
|
|
|
|
if (detail.itemsPerPage != itemsPerPage) {
|
2023-05-12 11:05:39 +02:00
|
|
|
updateConfiguration(
|
|
|
|
detail.itemsPerPage.toString(),
|
|
|
|
detail.page
|
|
|
|
)
|
2023-05-05 17:01:50 +02:00
|
|
|
} else {
|
|
|
|
paging = { itemsPerPage: detail.itemsPerPage, page: detail.page }
|
2022-06-22 11:20:57 +02:00
|
|
|
}
|
2023-05-05 10:07:12 +02:00
|
|
|
}}
|
|
|
|
/>
|
2022-06-22 11:20:57 +02:00
|
|
|
|
|
|
|
<style>
|
|
|
|
.cc-table-wrapper {
|
|
|
|
overflow: initial;
|
|
|
|
}
|
|
|
|
|
|
|
|
.cc-table-wrapper > :global(table) {
|
|
|
|
border-collapse: separate;
|
|
|
|
border-spacing: 0px;
|
|
|
|
table-layout: fixed;
|
|
|
|
}
|
|
|
|
|
|
|
|
.cc-table-wrapper :global(button) {
|
|
|
|
margin-bottom: 0px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.cc-table-wrapper > :global(table > tbody > tr > td) {
|
|
|
|
margin: 0px;
|
|
|
|
padding-left: 5px;
|
|
|
|
padding-right: 0px;
|
|
|
|
}
|
|
|
|
|
|
|
|
th.position-sticky.top-0 {
|
|
|
|
background-color: white;
|
|
|
|
z-index: 10;
|
|
|
|
border-bottom: 1px solid black;
|
|
|
|
}
|
|
|
|
</style>
|