|
|
|
@@ -6,6 +6,7 @@
|
|
|
|
|
- `filterPresets Object?`: Optional predefined filter values [Default: {}]
|
|
|
|
|
- `disableClusterSelection Bool?`: Is the selection disabled [Default: false]
|
|
|
|
|
- `startTimeQuickSelect Bool?`: Render startTime quick selections [Default: false]
|
|
|
|
|
- `matchedJobs Number?`: Number of jobs matching the filter [Default: -2]
|
|
|
|
|
|
|
|
|
|
Events:
|
|
|
|
|
- `update-filters, {filters: [Object]?}`: The detail's 'filters' prop are new filter items to be applied
|
|
|
|
@@ -17,11 +18,11 @@
|
|
|
|
|
<script>
|
|
|
|
|
import { createEventDispatcher } from "svelte";
|
|
|
|
|
import {
|
|
|
|
|
Row,
|
|
|
|
|
Col,
|
|
|
|
|
DropdownItem,
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownToggle,
|
|
|
|
|
Button,
|
|
|
|
|
ButtonGroup,
|
|
|
|
|
ButtonDropdown,
|
|
|
|
|
Icon,
|
|
|
|
|
} from "@sveltestrap/sveltestrap";
|
|
|
|
@@ -42,6 +43,7 @@
|
|
|
|
|
export let filterPresets = {};
|
|
|
|
|
export let disableClusterSelection = false;
|
|
|
|
|
export let startTimeQuickSelect = false;
|
|
|
|
|
export let matchedJobs = -2;
|
|
|
|
|
|
|
|
|
|
let filters = {
|
|
|
|
|
projectMatch: filterPresets.projectMatch || "contains",
|
|
|
|
@@ -217,170 +219,174 @@
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<Row>
|
|
|
|
|
<Col xs="auto">
|
|
|
|
|
<ButtonDropdown class="cc-dropdown-on-hover">
|
|
|
|
|
<DropdownToggle outline caret color="success">
|
|
|
|
|
<Icon name="sliders" />
|
|
|
|
|
Filters
|
|
|
|
|
</DropdownToggle>
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownItem header>Manage Filters</DropdownItem>
|
|
|
|
|
{#if menuText}
|
|
|
|
|
<DropdownItem disabled>{menuText}</DropdownItem>
|
|
|
|
|
<DropdownItem divider />
|
|
|
|
|
{/if}
|
|
|
|
|
<DropdownItem on:click={() => (isClusterOpen = true)}>
|
|
|
|
|
<Icon name="cpu" /> Cluster/Partition
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isJobStatesOpen = true)}>
|
|
|
|
|
<Icon name="gear-fill" /> Job States
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isStartTimeOpen = true)}>
|
|
|
|
|
<Icon name="calendar-range" /> Start Time
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
<Icon name="stopwatch" /> Duration
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isTagsOpen = true)}>
|
|
|
|
|
<Icon name="tags" /> Tags
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isResourcesOpen = true)}>
|
|
|
|
|
<Icon name="hdd-stack" /> Resources
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isEnergyOpen = true)}>
|
|
|
|
|
<Icon name="lightning-charge-fill" /> Energy
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isStatsOpen = true)}>
|
|
|
|
|
<Icon name="bar-chart" on:click={() => (isStatsOpen = true)} /> Statistics
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
{#if startTimeQuickSelect}
|
|
|
|
|
<DropdownItem divider />
|
|
|
|
|
<DropdownItem disabled>Start Time Qick Selection</DropdownItem>
|
|
|
|
|
{#each [{ text: "Last 6hrs", url: "last6h", seconds: 6 * 60 * 60 }, { text: "Last 24hrs", url: "last24h", seconds: 24 * 60 * 60 }, { text: "Last 7 days", url: "last7d", seconds: 7 * 24 * 60 * 60 }, { text: "Last 30 days", url: "last30d", seconds: 30 * 24 * 60 * 60 }] as { text, url, seconds }}
|
|
|
|
|
<DropdownItem
|
|
|
|
|
on:click={() => {
|
|
|
|
|
filters.startTime.from = new Date(
|
|
|
|
|
Date.now() - seconds * 1000,
|
|
|
|
|
).toISOString();
|
|
|
|
|
filters.startTime.to = new Date(Date.now()).toISOString();
|
|
|
|
|
(filters.startTime.text = text), (filters.startTime.url = url);
|
|
|
|
|
updateFilters();
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Icon name="calendar-range" />
|
|
|
|
|
{text}
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
{/each}
|
|
|
|
|
{/if}
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</ButtonDropdown>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col xs="auto">
|
|
|
|
|
{#if filters.cluster}
|
|
|
|
|
<Info icon="cpu" on:click={() => (isClusterOpen = true)}>
|
|
|
|
|
{filters.cluster}
|
|
|
|
|
{#if filters.partition}
|
|
|
|
|
({filters.partition})
|
|
|
|
|
{/if}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.states.length != allJobStates.length}
|
|
|
|
|
<Info icon="gear-fill" on:click={() => (isJobStatesOpen = true)}>
|
|
|
|
|
{filters.states.join(", ")}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.startTime.from || filters.startTime.to}
|
|
|
|
|
<Info icon="calendar-range" on:click={() => (isStartTimeOpen = true)}>
|
|
|
|
|
{#if filters.startTime.text}
|
|
|
|
|
{filters.startTime.text}
|
|
|
|
|
{:else}
|
|
|
|
|
{new Date(filters.startTime.from).toLocaleString()} - {new Date(
|
|
|
|
|
filters.startTime.to,
|
|
|
|
|
).toLocaleString()}
|
|
|
|
|
{/if}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.duration.from || filters.duration.to}
|
|
|
|
|
<Info icon="stopwatch" on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
{Math.floor(filters.duration.from / 3600)}h:{Math.floor(
|
|
|
|
|
(filters.duration.from % 3600) / 60,
|
|
|
|
|
)}m -
|
|
|
|
|
{Math.floor(filters.duration.to / 3600)}h:{Math.floor(
|
|
|
|
|
(filters.duration.to % 3600) / 60,
|
|
|
|
|
)}m
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.duration.lessThan}
|
|
|
|
|
<Info icon="stopwatch" on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
Duration less than {Math.floor(
|
|
|
|
|
filters.duration.lessThan / 3600,
|
|
|
|
|
)}h:{Math.floor((filters.duration.lessThan % 3600) / 60)}m
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.duration.moreThan}
|
|
|
|
|
<Info icon="stopwatch" on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
Duration more than {Math.floor(
|
|
|
|
|
filters.duration.moreThan / 3600,
|
|
|
|
|
)}h:{Math.floor((filters.duration.moreThan % 3600) / 60)}m
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.tags.length != 0}
|
|
|
|
|
<Info icon="tags" on:click={() => (isTagsOpen = true)}>
|
|
|
|
|
{#each filters.tags as tagId}
|
|
|
|
|
{#key tagId}
|
|
|
|
|
<Tag id={tagId} clickable={false} />
|
|
|
|
|
{/key}
|
|
|
|
|
<!-- Dropdown-Button -->
|
|
|
|
|
<ButtonGroup>
|
|
|
|
|
<ButtonDropdown class="cc-dropdown-on-hover mb-1" style="{(matchedJobs >= -1) ? '' : 'margin-right: 0.5rem;'}">
|
|
|
|
|
<DropdownToggle outline caret color="success">
|
|
|
|
|
<Icon name="sliders" />
|
|
|
|
|
Filters
|
|
|
|
|
</DropdownToggle>
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownItem header>Manage Filters</DropdownItem>
|
|
|
|
|
{#if menuText}
|
|
|
|
|
<DropdownItem disabled>{menuText}</DropdownItem>
|
|
|
|
|
<DropdownItem divider />
|
|
|
|
|
{/if}
|
|
|
|
|
<DropdownItem on:click={() => (isClusterOpen = true)}>
|
|
|
|
|
<Icon name="cpu" /> Cluster/Partition
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isJobStatesOpen = true)}>
|
|
|
|
|
<Icon name="gear-fill" /> Job States
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isStartTimeOpen = true)}>
|
|
|
|
|
<Icon name="calendar-range" /> Start Time
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
<Icon name="stopwatch" /> Duration
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isTagsOpen = true)}>
|
|
|
|
|
<Icon name="tags" /> Tags
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isResourcesOpen = true)}>
|
|
|
|
|
<Icon name="hdd-stack" /> Resources
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isEnergyOpen = true)}>
|
|
|
|
|
<Icon name="lightning-charge-fill" /> Energy
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
<DropdownItem on:click={() => (isStatsOpen = true)}>
|
|
|
|
|
<Icon name="bar-chart" on:click={() => (isStatsOpen = true)} /> Statistics
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
{#if startTimeQuickSelect}
|
|
|
|
|
<DropdownItem divider />
|
|
|
|
|
<DropdownItem disabled>Start Time Quick Selection</DropdownItem>
|
|
|
|
|
{#each [{ text: "Last 6hrs", url: "last6h", seconds: 6 * 60 * 60 }, { text: "Last 24hrs", url: "last24h", seconds: 24 * 60 * 60 }, { text: "Last 7 days", url: "last7d", seconds: 7 * 24 * 60 * 60 }, { text: "Last 30 days", url: "last30d", seconds: 30 * 24 * 60 * 60 }] as { text, url, seconds }}
|
|
|
|
|
<DropdownItem
|
|
|
|
|
on:click={() => {
|
|
|
|
|
filters.startTime.from = new Date(
|
|
|
|
|
Date.now() - seconds * 1000,
|
|
|
|
|
).toISOString();
|
|
|
|
|
filters.startTime.to = new Date(Date.now()).toISOString();
|
|
|
|
|
(filters.startTime.text = text), (filters.startTime.url = url);
|
|
|
|
|
updateFilters();
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Icon name="calendar-range" />
|
|
|
|
|
{text}
|
|
|
|
|
</DropdownItem>
|
|
|
|
|
{/each}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
{/if}
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</ButtonDropdown>
|
|
|
|
|
{#if matchedJobs >= -1}
|
|
|
|
|
<Button class="mb-1" style="margin-right: 0.5rem;" disabled outline>
|
|
|
|
|
{matchedJobs == -1 ? 'Loading ...' : `${matchedJobs} jobs`}
|
|
|
|
|
</Button>
|
|
|
|
|
{/if}
|
|
|
|
|
</ButtonGroup>
|
|
|
|
|
|
|
|
|
|
{#if filters.numNodes.from != null || filters.numNodes.to != null || filters.numHWThreads.from != null || filters.numHWThreads.to != null || filters.numAccelerators.from != null || filters.numAccelerators.to != null}
|
|
|
|
|
<Info icon="hdd-stack" on:click={() => (isResourcesOpen = true)}>
|
|
|
|
|
{#if isNodesModified}
|
|
|
|
|
Nodes: {filters.numNodes.from} - {filters.numNodes.to}
|
|
|
|
|
{/if}
|
|
|
|
|
{#if isNodesModified && isHwthreadsModified},
|
|
|
|
|
{/if}
|
|
|
|
|
{#if isHwthreadsModified}
|
|
|
|
|
HWThreads: {filters.numHWThreads.from} - {filters.numHWThreads.to}
|
|
|
|
|
{/if}
|
|
|
|
|
{#if (isNodesModified || isHwthreadsModified) && isAccsModified},
|
|
|
|
|
{/if}
|
|
|
|
|
{#if isAccsModified}
|
|
|
|
|
Accelerators: {filters.numAccelerators.from} - {filters
|
|
|
|
|
.numAccelerators.to}
|
|
|
|
|
{/if}
|
|
|
|
|
</Info>
|
|
|
|
|
<!-- SELECTED FILTER PILLS -->
|
|
|
|
|
{#if filters.cluster}
|
|
|
|
|
<Info icon="cpu" on:click={() => (isClusterOpen = true)}>
|
|
|
|
|
{filters.cluster}
|
|
|
|
|
{#if filters.partition}
|
|
|
|
|
({filters.partition})
|
|
|
|
|
{/if}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.node != null}
|
|
|
|
|
<Info icon="hdd-stack" on:click={() => (isResourcesOpen = true)}>
|
|
|
|
|
Node: {filters.node}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
{#if filters.states.length != allJobStates.length}
|
|
|
|
|
<Info icon="gear-fill" on:click={() => (isJobStatesOpen = true)}>
|
|
|
|
|
{filters.states.join(", ")}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.energy.from || filters.energy.to}
|
|
|
|
|
<Info icon="lightning-charge-fill" on:click={() => (isEnergyOpen = true)}>
|
|
|
|
|
Total Energy: {filters.energy.from} - {filters.energy.to}
|
|
|
|
|
</Info>
|
|
|
|
|
{#if filters.startTime.from || filters.startTime.to}
|
|
|
|
|
<Info icon="calendar-range" on:click={() => (isStartTimeOpen = true)}>
|
|
|
|
|
{#if filters.startTime.text}
|
|
|
|
|
{filters.startTime.text}
|
|
|
|
|
{:else}
|
|
|
|
|
{new Date(filters.startTime.from).toLocaleString()} - {new Date(
|
|
|
|
|
filters.startTime.to,
|
|
|
|
|
).toLocaleString()}
|
|
|
|
|
{/if}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.stats.length > 0}
|
|
|
|
|
<Info icon="bar-chart" on:click={() => (isStatsOpen = true)}>
|
|
|
|
|
{filters.stats
|
|
|
|
|
.map((stat) => `${stat.text}: ${stat.from} - ${stat.to}`)
|
|
|
|
|
.join(", ")}
|
|
|
|
|
</Info>
|
|
|
|
|
{#if filters.duration.from || filters.duration.to}
|
|
|
|
|
<Info icon="stopwatch" on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
{Math.floor(filters.duration.from / 3600)}h:{Math.floor(
|
|
|
|
|
(filters.duration.from % 3600) / 60,
|
|
|
|
|
)}m -
|
|
|
|
|
{Math.floor(filters.duration.to / 3600)}h:{Math.floor(
|
|
|
|
|
(filters.duration.to % 3600) / 60,
|
|
|
|
|
)}m
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.duration.lessThan}
|
|
|
|
|
<Info icon="stopwatch" on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
Duration less than {Math.floor(
|
|
|
|
|
filters.duration.lessThan / 3600,
|
|
|
|
|
)}h:{Math.floor((filters.duration.lessThan % 3600) / 60)}m
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.duration.moreThan}
|
|
|
|
|
<Info icon="stopwatch" on:click={() => (isDurationOpen = true)}>
|
|
|
|
|
Duration more than {Math.floor(
|
|
|
|
|
filters.duration.moreThan / 3600,
|
|
|
|
|
)}h:{Math.floor((filters.duration.moreThan % 3600) / 60)}m
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.tags.length != 0}
|
|
|
|
|
<Info icon="tags" on:click={() => (isTagsOpen = true)}>
|
|
|
|
|
{#each filters.tags as tagId}
|
|
|
|
|
{#key tagId}
|
|
|
|
|
<Tag id={tagId} clickable={false} />
|
|
|
|
|
{/key}
|
|
|
|
|
{/each}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.numNodes.from != null || filters.numNodes.to != null || filters.numHWThreads.from != null || filters.numHWThreads.to != null || filters.numAccelerators.from != null || filters.numAccelerators.to != null}
|
|
|
|
|
<Info icon="hdd-stack" on:click={() => (isResourcesOpen = true)}>
|
|
|
|
|
{#if isNodesModified}
|
|
|
|
|
Nodes: {filters.numNodes.from} - {filters.numNodes.to}
|
|
|
|
|
{/if}
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
{#if isNodesModified && isHwthreadsModified},
|
|
|
|
|
{/if}
|
|
|
|
|
{#if isHwthreadsModified}
|
|
|
|
|
HWThreads: {filters.numHWThreads.from} - {filters.numHWThreads.to}
|
|
|
|
|
{/if}
|
|
|
|
|
{#if (isNodesModified || isHwthreadsModified) && isAccsModified},
|
|
|
|
|
{/if}
|
|
|
|
|
{#if isAccsModified}
|
|
|
|
|
Accelerators: {filters.numAccelerators.from} - {filters
|
|
|
|
|
.numAccelerators.to}
|
|
|
|
|
{/if}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.node != null}
|
|
|
|
|
<Info icon="hdd-stack" on:click={() => (isResourcesOpen = true)}>
|
|
|
|
|
Node: {filters.node}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.energy.from || filters.energy.to}
|
|
|
|
|
<Info icon="lightning-charge-fill" on:click={() => (isEnergyOpen = true)}>
|
|
|
|
|
Total Energy: {filters.energy.from} - {filters.energy.to}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if filters.stats.length > 0}
|
|
|
|
|
<Info icon="bar-chart" on:click={() => (isStatsOpen = true)}>
|
|
|
|
|
{filters.stats
|
|
|
|
|
.map((stat) => `${stat.text}: ${stat.from} - ${stat.to}`)
|
|
|
|
|
.join(", ")}
|
|
|
|
|
</Info>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<Cluster
|
|
|
|
|
{disableClusterSelection}
|
|
|
|
|