mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-25 04:49:05 +01:00
add energy filterr in new component
This commit is contained in:
parent
615281601c
commit
6019891591
@ -265,6 +265,7 @@ input JobFilter {
|
|||||||
cluster: StringInput
|
cluster: StringInput
|
||||||
partition: StringInput
|
partition: StringInput
|
||||||
duration: IntRange
|
duration: IntRange
|
||||||
|
energy: FloatRange
|
||||||
|
|
||||||
minRunningFor: Int
|
minRunningFor: Int
|
||||||
|
|
||||||
|
@ -2153,6 +2153,7 @@ input JobFilter {
|
|||||||
cluster: StringInput
|
cluster: StringInput
|
||||||
partition: StringInput
|
partition: StringInput
|
||||||
duration: IntRange
|
duration: IntRange
|
||||||
|
energy: FloatRange
|
||||||
|
|
||||||
minRunningFor: Int
|
minRunningFor: Int
|
||||||
|
|
||||||
@ -13452,7 +13453,7 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj int
|
|||||||
asMap[k] = v
|
asMap[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldsInOrder := [...]string{"tags", "jobId", "arrayJobId", "user", "project", "jobName", "cluster", "partition", "duration", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "metricStats", "exclusive", "node"}
|
fieldsInOrder := [...]string{"tags", "jobId", "arrayJobId", "user", "project", "jobName", "cluster", "partition", "duration", "energy", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "metricStats", "exclusive", "node"}
|
||||||
for _, k := range fieldsInOrder {
|
for _, k := range fieldsInOrder {
|
||||||
v, ok := asMap[k]
|
v, ok := asMap[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -13522,6 +13523,13 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj int
|
|||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
it.Duration = data
|
it.Duration = data
|
||||||
|
case "energy":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("energy"))
|
||||||
|
data, err := ec.unmarshalOFloatRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐFloatRange(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.Energy = data
|
||||||
case "minRunningFor":
|
case "minRunningFor":
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("minRunningFor"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("minRunningFor"))
|
||||||
data, err := ec.unmarshalOInt2ᚖint(ctx, v)
|
data, err := ec.unmarshalOInt2ᚖint(ctx, v)
|
||||||
@ -18550,6 +18558,14 @@ func (ec *executionContext) marshalOFloat2float64(ctx context.Context, sel ast.S
|
|||||||
return graphql.WrapContextMarshaler(ctx, res)
|
return graphql.WrapContextMarshaler(ctx, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalOFloatRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐFloatRange(ctx context.Context, v interface{}) (*model.FloatRange, error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
res, err := ec.unmarshalInputFloatRange(ctx, v)
|
||||||
|
return &res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalOFootprintValue2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐFootprintValue(ctx context.Context, sel ast.SelectionSet, v []*model.FootprintValue) graphql.Marshaler {
|
func (ec *executionContext) marshalOFootprintValue2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐFootprintValue(ctx context.Context, sel ast.SelectionSet, v []*model.FootprintValue) graphql.Marshaler {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
@ -58,6 +58,7 @@ type JobFilter struct {
|
|||||||
Cluster *StringInput `json:"cluster,omitempty"`
|
Cluster *StringInput `json:"cluster,omitempty"`
|
||||||
Partition *StringInput `json:"partition,omitempty"`
|
Partition *StringInput `json:"partition,omitempty"`
|
||||||
Duration *schema.IntRange `json:"duration,omitempty"`
|
Duration *schema.IntRange `json:"duration,omitempty"`
|
||||||
|
Energy *FloatRange `json:"energy,omitempty"`
|
||||||
MinRunningFor *int `json:"minRunningFor,omitempty"`
|
MinRunningFor *int `json:"minRunningFor,omitempty"`
|
||||||
NumNodes *schema.IntRange `json:"numNodes,omitempty"`
|
NumNodes *schema.IntRange `json:"numNodes,omitempty"`
|
||||||
NumAccelerators *schema.IntRange `json:"numAccelerators,omitempty"`
|
NumAccelerators *schema.IntRange `json:"numAccelerators,omitempty"`
|
||||||
|
@ -192,6 +192,9 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
|
|||||||
if filter.Node != nil {
|
if filter.Node != nil {
|
||||||
query = buildStringCondition("job.resources", filter.Node, query)
|
query = buildStringCondition("job.resources", filter.Node, query)
|
||||||
}
|
}
|
||||||
|
if filter.Energy != nil {
|
||||||
|
query = buildFloatCondition("job.energy", filter.Energy, query)
|
||||||
|
}
|
||||||
if filter.MetricStats != nil {
|
if filter.MetricStats != nil {
|
||||||
for _, ms := range filter.MetricStats {
|
for _, ms := range filter.MetricStats {
|
||||||
query = buildFloatJsonCondition(ms.MetricName, ms.Range, query)
|
query = buildFloatJsonCondition(ms.MetricName, ms.Range, query)
|
||||||
@ -204,6 +207,10 @@ func buildIntCondition(field string, cond *schema.IntRange, query sq.SelectBuild
|
|||||||
return query.Where(field+" BETWEEN ? AND ?", cond.From, cond.To)
|
return query.Where(field+" BETWEEN ? AND ?", cond.From, cond.To)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildFloatCondition(field string, cond *model.FloatRange, query sq.SelectBuilder) sq.SelectBuilder {
|
||||||
|
return query.Where(field+" BETWEEN ? AND ?", cond.From, cond.To)
|
||||||
|
}
|
||||||
|
|
||||||
func buildTimeCondition(field string, cond *schema.TimeRange, query sq.SelectBuilder) sq.SelectBuilder {
|
func buildTimeCondition(field string, cond *schema.TimeRange, query sq.SelectBuilder) sq.SelectBuilder {
|
||||||
if cond.From != nil && cond.To != nil {
|
if cond.From != nil && cond.To != nil {
|
||||||
return query.Where(field+" BETWEEN ? AND ?", cond.From.Unix(), cond.To.Unix())
|
return query.Where(field+" BETWEEN ? AND ?", cond.From.Unix(), cond.To.Unix())
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
import StartTime from "./filters/StartTime.svelte";
|
import StartTime from "./filters/StartTime.svelte";
|
||||||
import Tags from "./filters/Tags.svelte";
|
import Tags from "./filters/Tags.svelte";
|
||||||
import Duration from "./filters/Duration.svelte";
|
import Duration from "./filters/Duration.svelte";
|
||||||
|
import Energy from "./filters/Energy.svelte";
|
||||||
import Resources from "./filters/Resources.svelte";
|
import Resources from "./filters/Resources.svelte";
|
||||||
import Statistics from "./filters/Stats.svelte";
|
import Statistics from "./filters/Stats.svelte";
|
||||||
|
|
||||||
@ -68,6 +69,7 @@
|
|||||||
jobName: filterPresets.jobName || "",
|
jobName: filterPresets.jobName || "",
|
||||||
|
|
||||||
node: filterPresets.node || null,
|
node: filterPresets.node || null,
|
||||||
|
energy: filterPresets.energy || { from: null, to: null },
|
||||||
numNodes: filterPresets.numNodes || { from: null, to: null },
|
numNodes: filterPresets.numNodes || { from: null, to: null },
|
||||||
numHWThreads: filterPresets.numHWThreads || { from: null, to: null },
|
numHWThreads: filterPresets.numHWThreads || { from: null, to: null },
|
||||||
numAccelerators: filterPresets.numAccelerators || { from: null, to: null },
|
numAccelerators: filterPresets.numAccelerators || { from: null, to: null },
|
||||||
@ -80,6 +82,7 @@
|
|||||||
isStartTimeOpen = false,
|
isStartTimeOpen = false,
|
||||||
isTagsOpen = false,
|
isTagsOpen = false,
|
||||||
isDurationOpen = false,
|
isDurationOpen = false,
|
||||||
|
isEnergyOpen = false,
|
||||||
isResourcesOpen = false,
|
isResourcesOpen = false,
|
||||||
isStatsOpen = false,
|
isStatsOpen = false,
|
||||||
isNodesModified = false,
|
isNodesModified = false,
|
||||||
@ -110,6 +113,10 @@
|
|||||||
items.push({ duration: { from: 0, to: filters.duration.lessThan } });
|
items.push({ duration: { from: 0, to: filters.duration.lessThan } });
|
||||||
if (filters.duration.moreThan)
|
if (filters.duration.moreThan)
|
||||||
items.push({ duration: { from: filters.duration.moreThan, to: 604800 } }); // 7 days to include special jobs with long runtimes
|
items.push({ duration: { from: filters.duration.moreThan, to: 604800 } }); // 7 days to include special jobs with long runtimes
|
||||||
|
if (filters.energy.from || filters.energy.to)
|
||||||
|
items.push({
|
||||||
|
energy: { from: filters.energy.from, to: filters.energy.to },
|
||||||
|
});
|
||||||
if (filters.jobId)
|
if (filters.jobId)
|
||||||
items.push({ jobId: { [filters.jobIdMatch]: filters.jobId } });
|
items.push({ jobId: { [filters.jobIdMatch]: filters.jobId } });
|
||||||
if (filters.arrayJobId != null)
|
if (filters.arrayJobId != null)
|
||||||
@ -181,6 +188,8 @@
|
|||||||
opts.push(`duration=0-${filters.duration.lessThan}`);
|
opts.push(`duration=0-${filters.duration.lessThan}`);
|
||||||
if (filters.duration.moreThan)
|
if (filters.duration.moreThan)
|
||||||
opts.push(`duration=${filters.duration.moreThan}-604800`);
|
opts.push(`duration=${filters.duration.moreThan}-604800`);
|
||||||
|
if (filters.energy.from && filters.energy.to)
|
||||||
|
opts.push(`energy=${filters.energy.from}-${filters.energy.to}`);
|
||||||
if (filters.numNodes.from && filters.numNodes.to)
|
if (filters.numNodes.from && filters.numNodes.to)
|
||||||
opts.push(`numNodes=${filters.numNodes.from}-${filters.numNodes.to}`);
|
opts.push(`numNodes=${filters.numNodes.from}-${filters.numNodes.to}`);
|
||||||
if (filters.numAccelerators.from && filters.numAccelerators.to)
|
if (filters.numAccelerators.from && filters.numAccelerators.to)
|
||||||
@ -239,6 +248,9 @@
|
|||||||
<DropdownItem on:click={() => (isResourcesOpen = true)}>
|
<DropdownItem on:click={() => (isResourcesOpen = true)}>
|
||||||
<Icon name="hdd-stack" /> Resources
|
<Icon name="hdd-stack" /> Resources
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
|
<DropdownItem on:click={() => (isEnergyOpen = true)}>
|
||||||
|
<Icon name="lightning-charge-fill" /> Energy
|
||||||
|
</DropdownItem>
|
||||||
<DropdownItem on:click={() => (isStatsOpen = true)}>
|
<DropdownItem on:click={() => (isStatsOpen = true)}>
|
||||||
<Icon name="bar-chart" on:click={() => (isStatsOpen = true)} /> Statistics
|
<Icon name="bar-chart" on:click={() => (isStatsOpen = true)} /> Statistics
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
@ -354,6 +366,12 @@
|
|||||||
</Info>
|
</Info>
|
||||||
{/if}
|
{/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}
|
{#if filters.stats.length > 0}
|
||||||
<Info icon="bar-chart" on:click={() => (isStatsOpen = true)}>
|
<Info icon="bar-chart" on:click={() => (isStatsOpen = true)}>
|
||||||
{filters.stats
|
{filters.stats
|
||||||
@ -423,6 +441,12 @@
|
|||||||
on:set-filter={() => updateFilters()}
|
on:set-filter={() => updateFilters()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Energy
|
||||||
|
bind:isOpen={isEnergyOpen}
|
||||||
|
bind:energy={filters.energy}
|
||||||
|
on:set-filter={() => updateFilters()}
|
||||||
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:global(.cc-dropdown-on-hover:hover .dropdown-menu) {
|
:global(.cc-dropdown-on-hover:hover .dropdown-menu) {
|
||||||
display: block;
|
display: block;
|
||||||
|
70
web/frontend/src/generic/filters/Energy.svelte
Normal file
70
web/frontend/src/generic/filters/Energy.svelte
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<!--
|
||||||
|
@component Filter sub-component for selecting job energies
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
- `isOpen Bool?`: Is this filter component opened [Default: false]
|
||||||
|
- `energy Object?`: The currently selected total energy filter [Default: {from:null, to:null}]
|
||||||
|
|
||||||
|
Events:
|
||||||
|
- `set-filter, {Object}`: Set 'energy' filter in upstream component
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalHeader,
|
||||||
|
ModalFooter,
|
||||||
|
} from "@sveltestrap/sveltestrap";
|
||||||
|
import DoubleRangeSlider from "../select/DoubleRangeSlider.svelte";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
const energyMaximum = 1000.0;
|
||||||
|
|
||||||
|
export let isOpen = false;
|
||||||
|
export let energy= {from: null, to: null};
|
||||||
|
|
||||||
|
function resetRanges() {
|
||||||
|
energy.from = null
|
||||||
|
energy.to = null
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal {isOpen} toggle={() => (isOpen = !isOpen)}>
|
||||||
|
<ModalHeader>Filter based on energy</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<h4>Total Job Energy (kWh)</h4>
|
||||||
|
<DoubleRangeSlider
|
||||||
|
on:change={({ detail }) => (
|
||||||
|
(energy.from = detail[0]), (energy.to = detail[1])
|
||||||
|
)}
|
||||||
|
min={0.0}
|
||||||
|
max={energyMaximum}
|
||||||
|
firstSlider={energy?.from ? energy.from : 0.0}
|
||||||
|
secondSlider={energy?.to ? energy.to : energyMaximum}
|
||||||
|
inputFieldFrom={energy?.from ? energy.from : null}
|
||||||
|
inputFieldTo={energy?.to ? energy.to : null}
|
||||||
|
/>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
on:click={() => {
|
||||||
|
isOpen = false;
|
||||||
|
dispatch("set-filter", { energy });
|
||||||
|
}}>Close & Apply</Button
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="danger"
|
||||||
|
on:click={() => {
|
||||||
|
isOpen = false;
|
||||||
|
resetRanges();
|
||||||
|
dispatch("set-filter", { energy });
|
||||||
|
}}>Reset</Button
|
||||||
|
>
|
||||||
|
<Button on:click={() => (isOpen = false)}>Close</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
@ -55,7 +55,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal {isOpen} toggle={() => (isOpen = !isOpen)}>
|
<Modal {isOpen} toggle={() => (isOpen = !isOpen)}>
|
||||||
<ModalHeader>Filter based on statistics (of non-running jobs)</ModalHeader>
|
<ModalHeader>Filter based on statistics</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
{#each statistics as stat}
|
{#each statistics as stat}
|
||||||
<h4>{stat.text}</h4>
|
<h4>{stat.text}</h4>
|
||||||
|
Loading…
Reference in New Issue
Block a user