mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-05-14 05:01:41 +02:00
add manual job selection for comparison in jobs view
This commit is contained in:
parent
4419df8d1b
commit
69286881e4
@ -300,8 +300,8 @@ type TimeRangeOutput { range: String, from: Time!, to: Time! }
|
|||||||
|
|
||||||
input JobFilter {
|
input JobFilter {
|
||||||
tags: [ID!]
|
tags: [ID!]
|
||||||
|
dbId: [ID!]
|
||||||
jobId: StringInput
|
jobId: StringInput
|
||||||
jobIds: [ID!]
|
|
||||||
arrayJobId: Int
|
arrayJobId: Int
|
||||||
user: StringInput
|
user: StringInput
|
||||||
project: StringInput
|
project: StringInput
|
||||||
|
@ -2465,8 +2465,8 @@ type TimeRangeOutput { range: String, from: Time!, to: Time! }
|
|||||||
|
|
||||||
input JobFilter {
|
input JobFilter {
|
||||||
tags: [ID!]
|
tags: [ID!]
|
||||||
|
dbId: [ID!]
|
||||||
jobId: StringInput
|
jobId: StringInput
|
||||||
jobIds: [ID!]
|
|
||||||
arrayJobId: Int
|
arrayJobId: Int
|
||||||
user: StringInput
|
user: StringInput
|
||||||
project: StringInput
|
project: StringInput
|
||||||
@ -16447,7 +16447,7 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj any
|
|||||||
asMap[k] = v
|
asMap[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldsInOrder := [...]string{"tags", "jobId", "jobIds", "arrayJobId", "user", "project", "jobName", "cluster", "partition", "duration", "energy", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "metricStats", "exclusive", "node"}
|
fieldsInOrder := [...]string{"tags", "dbId", "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 {
|
||||||
@ -16461,6 +16461,13 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj any
|
|||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
it.Tags = data
|
it.Tags = data
|
||||||
|
case "dbId":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dbId"))
|
||||||
|
data, err := ec.unmarshalOID2ᚕstringᚄ(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.DbID = data
|
||||||
case "jobId":
|
case "jobId":
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jobId"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jobId"))
|
||||||
data, err := ec.unmarshalOStringInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐStringInput(ctx, v)
|
data, err := ec.unmarshalOStringInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐStringInput(ctx, v)
|
||||||
@ -16468,13 +16475,6 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj any
|
|||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
it.JobID = data
|
it.JobID = data
|
||||||
case "jobIds":
|
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jobIds"))
|
|
||||||
data, err := ec.unmarshalOID2ᚕstringᚄ(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
it.JobIds = data
|
|
||||||
case "arrayJobId":
|
case "arrayJobId":
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("arrayJobId"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("arrayJobId"))
|
||||||
data, err := ec.unmarshalOInt2ᚖint(ctx, v)
|
data, err := ec.unmarshalOInt2ᚖint(ctx, v)
|
||||||
|
@ -50,8 +50,8 @@ type IntRangeOutput struct {
|
|||||||
|
|
||||||
type JobFilter struct {
|
type JobFilter struct {
|
||||||
Tags []string `json:"tags,omitempty"`
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
DbID []string `json:"dbId,omitempty"`
|
||||||
JobID *StringInput `json:"jobId,omitempty"`
|
JobID *StringInput `json:"jobId,omitempty"`
|
||||||
JobIds []string `json:"jobIds,omitempty"`
|
|
||||||
ArrayJobID *int `json:"arrayJobId,omitempty"`
|
ArrayJobID *int `json:"arrayJobId,omitempty"`
|
||||||
User *StringInput `json:"user,omitempty"`
|
User *StringInput `json:"user,omitempty"`
|
||||||
Project *StringInput `json:"project,omitempty"`
|
Project *StringInput `json:"project,omitempty"`
|
||||||
|
@ -146,17 +146,16 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
|
|||||||
// This is an OR-Logic query: Returns all distinct jobs with at least one of the requested tags; TODO: AND-Logic query?
|
// This is an OR-Logic query: Returns all distinct jobs with at least one of the requested tags; TODO: AND-Logic query?
|
||||||
query = query.Join("jobtag ON jobtag.job_id = job.id").Where(sq.Eq{"jobtag.tag_id": filter.Tags}).Distinct()
|
query = query.Join("jobtag ON jobtag.job_id = job.id").Where(sq.Eq{"jobtag.tag_id": filter.Tags}).Distinct()
|
||||||
}
|
}
|
||||||
|
if filter.DbID != nil {
|
||||||
|
dbIDs := make([]string, len(filter.DbID))
|
||||||
|
for i, val := range filter.DbID {
|
||||||
|
dbIDs[i] = val
|
||||||
|
}
|
||||||
|
query = query.Where(sq.Eq{"job.id": dbIDs})
|
||||||
|
}
|
||||||
if filter.JobID != nil {
|
if filter.JobID != nil {
|
||||||
query = buildStringCondition("job.job_id", filter.JobID, query)
|
query = buildStringCondition("job.job_id", filter.JobID, query)
|
||||||
}
|
}
|
||||||
if filter.JobIds != nil {
|
|
||||||
jobIds := make([]string, len(filter.JobIds))
|
|
||||||
for i, val := range filter.JobIds {
|
|
||||||
jobIds[i] = string(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
query = query.Where(sq.Eq{"job.job_id": jobIds})
|
|
||||||
}
|
|
||||||
if filter.ArrayJobID != nil {
|
if filter.ArrayJobID != nil {
|
||||||
query = query.Where("job.array_job_id = ?", *filter.ArrayJobID)
|
query = query.Where("job.array_job_id = ?", *filter.ArrayJobID)
|
||||||
}
|
}
|
||||||
|
@ -297,6 +297,9 @@ func buildFilterPresets(query url.Values) map[string]interface{} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(query["dbId"]) != 0 {
|
||||||
|
filterPresets["dbId"] = query["dbId"]
|
||||||
|
}
|
||||||
if query.Get("jobId") != "" {
|
if query.Get("jobId") != "" {
|
||||||
if len(query["jobId"]) == 1 {
|
if len(query["jobId"]) == 1 {
|
||||||
filterPresets["jobId"] = query.Get("jobId")
|
filterPresets["jobId"] = query.Get("jobId")
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
let filterComponent; // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the
|
let filterComponent; // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the
|
||||||
let filterBuffer = [];
|
let filterBuffer = [];
|
||||||
|
let selectedJobs = [];
|
||||||
let jobList,
|
let jobList,
|
||||||
jobCompare,
|
jobCompare,
|
||||||
matchedListJobs,
|
matchedListJobs,
|
||||||
@ -59,6 +60,10 @@
|
|||||||
// so we need to wait for it to be ready before we can start a query.
|
// so we need to wait for it to be ready before we can start a query.
|
||||||
// This is also why JobList component starts out with a paused query.
|
// This is also why JobList component starts out with a paused query.
|
||||||
onMount(() => filterComponent.updateFilters());
|
onMount(() => filterComponent.updateFilters());
|
||||||
|
|
||||||
|
$: if (filterComponent && selectedJobs.length == 0) {
|
||||||
|
filterComponent.updateFilters({dbId: []})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- ROW1: Status-->
|
<!-- ROW1: Status-->
|
||||||
@ -80,7 +85,7 @@
|
|||||||
<Row cols={{ xs: 1, md: 2, lg: 5}} class="mb-3">
|
<Row cols={{ xs: 1, md: 2, lg: 5}} class="mb-3">
|
||||||
<Col lg="2" class="mb-2 mb-lg-0">
|
<Col lg="2" class="mb-2 mb-lg-0">
|
||||||
<ButtonGroup class="w-100">
|
<ButtonGroup class="w-100">
|
||||||
<Button outline color="primary" on:click={() => (isSortingOpen = true)}>
|
<Button outline color="primary" on:click={() => (isSortingOpen = true)} disabled={showCompare}>
|
||||||
<Icon name="sort-up" /> Sorting
|
<Icon name="sort-up" /> Sorting
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -130,11 +135,20 @@
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col lg="2" class="mb-2 mb-lg-0">
|
<Col lg="2" class="mb-2 mb-lg-0">
|
||||||
|
<ButtonGroup class="w-100">
|
||||||
<Button color="primary" on:click={() => {
|
<Button color="primary" on:click={() => {
|
||||||
|
if (selectedJobs.length != 0) filterComponent.updateFilters({dbId: selectedJobs})
|
||||||
|
else if (selectedJobs.length == 0) filterComponent.updateFilters({dbId: []})
|
||||||
showCompare = !showCompare
|
showCompare = !showCompare
|
||||||
}} >
|
}} >
|
||||||
{showCompare ? 'List' : 'Compare'} Jobs
|
{showCompare ? 'List' : 'Compare'} Jobs {selectedJobs.length != 0 ? `(${selectedJobs.length} selected)` : `(Use Filter)`}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button color="danger" disabled={selectedJobs.length == 0} on:click={() => {
|
||||||
|
selectedJobs = [] // Only empty array, filters handled by reactive reset
|
||||||
|
}}>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
@ -148,6 +162,7 @@
|
|||||||
bind:sorting
|
bind:sorting
|
||||||
bind:matchedListJobs
|
bind:matchedListJobs
|
||||||
bind:showFootprint
|
bind:showFootprint
|
||||||
|
bind:selectedJobs
|
||||||
{filterBuffer}
|
{filterBuffer}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
from: null,
|
from: null,
|
||||||
to: null,
|
to: null,
|
||||||
},
|
},
|
||||||
|
dbId: filterPresets.dbId || [],
|
||||||
jobId: filterPresets.jobId || "",
|
jobId: filterPresets.jobId || "",
|
||||||
arrayJobId: filterPresets.arrayJobId || null,
|
arrayJobId: filterPresets.arrayJobId || null,
|
||||||
user: filterPresets.user || "",
|
user: filterPresets.user || "",
|
||||||
@ -137,6 +138,8 @@
|
|||||||
items.push({
|
items.push({
|
||||||
energy: { from: filters.energy.from, to: filters.energy.to },
|
energy: { from: filters.energy.from, to: filters.energy.to },
|
||||||
});
|
});
|
||||||
|
if (filters.dbId.length != 0)
|
||||||
|
items.push({ dbId: filters.dbId });
|
||||||
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)
|
||||||
@ -180,7 +183,6 @@
|
|||||||
|
|
||||||
function changeURL() {
|
function changeURL() {
|
||||||
const dateToUnixEpoch = (rfc3339) => Math.floor(Date.parse(rfc3339) / 1000);
|
const dateToUnixEpoch = (rfc3339) => Math.floor(Date.parse(rfc3339) / 1000);
|
||||||
|
|
||||||
let opts = [];
|
let opts = [];
|
||||||
if (filters.cluster) opts.push(`cluster=${filters.cluster}`);
|
if (filters.cluster) opts.push(`cluster=${filters.cluster}`);
|
||||||
if (filters.node) opts.push(`node=${filters.node}`);
|
if (filters.node) opts.push(`node=${filters.node}`);
|
||||||
@ -196,6 +198,11 @@
|
|||||||
if (filters.startTime.range) {
|
if (filters.startTime.range) {
|
||||||
opts.push(`startTime=${filters.startTime.range}`)
|
opts.push(`startTime=${filters.startTime.range}`)
|
||||||
}
|
}
|
||||||
|
if (filters.dbId.length != 0) {
|
||||||
|
for (let dbi of filters.dbId) {
|
||||||
|
opts.push(`dbId=${dbi}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (filters.jobId.length != 0)
|
if (filters.jobId.length != 0)
|
||||||
if (filters.jobIdMatch != "in") {
|
if (filters.jobIdMatch != "in") {
|
||||||
opts.push(`jobId=${filters.jobId}`);
|
opts.push(`jobId=${filters.jobId}`);
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
export let metrics = ccconfig.plot_list_selectedMetrics;
|
export let metrics = ccconfig.plot_list_selectedMetrics;
|
||||||
export let showFootprint;
|
export let showFootprint;
|
||||||
export let filterBuffer = [];
|
export let filterBuffer = [];
|
||||||
|
export let selectedJobs = [];
|
||||||
|
|
||||||
let usePaging = ccconfig.job_list_usePaging
|
let usePaging = ccconfig.job_list_usePaging
|
||||||
let itemsPerPage = usePaging ? ccconfig.plot_list_jobsPerPage : 10;
|
let itemsPerPage = usePaging ? ccconfig.plot_list_jobsPerPage : 10;
|
||||||
@ -285,7 +286,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{:else}
|
{:else}
|
||||||
{#each jobs as job (job)}
|
{#each jobs as job (job)}
|
||||||
<JobListRow bind:triggerMetricRefresh {job} {metrics} {plotWidth} {showFootprint} />
|
<JobListRow bind:triggerMetricRefresh {job} {metrics} {plotWidth} {showFootprint} previousSelect={selectedJobs.includes(job.id)}
|
||||||
|
on:select-job={({detail}) => selectedJobs = [...selectedJobs, detail]}
|
||||||
|
on:unselect-job={({detail}) => selectedJobs = selectedJobs.filter(item => item !== detail)}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan={metrics.length + 1}> No jobs found </td>
|
<td colspan={metrics.length + 1}> No jobs found </td>
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
export let username = null;
|
export let username = null;
|
||||||
export let authlevel= null;
|
export let authlevel= null;
|
||||||
export let roles = null;
|
export let roles = null;
|
||||||
|
export let isSelected = null;
|
||||||
|
export let showSelect = false;
|
||||||
|
|
||||||
function formatDuration(duration) {
|
function formatDuration(duration) {
|
||||||
const hours = Math.floor(duration / 3600);
|
const hours = Math.floor(duration / 3600);
|
||||||
@ -76,6 +78,26 @@
|
|||||||
<a href="/monitoring/job/{job.id}" target="_blank">{job.jobId}</a>
|
<a href="/monitoring/job/{job.id}" target="_blank">{job.jobId}</a>
|
||||||
({job.cluster})
|
({job.cluster})
|
||||||
</span>
|
</span>
|
||||||
|
<span>
|
||||||
|
{#if showSelect}
|
||||||
|
<Button id={`${job.cluster}-${job.jobId}-select`} outline={!isSelected} color={isSelected? `success`: `secondary`} size="sm" class="mr-2"
|
||||||
|
on:click={() => {
|
||||||
|
isSelected = !isSelected
|
||||||
|
}}>
|
||||||
|
{#if isSelected}
|
||||||
|
<Icon name="check-square"/>
|
||||||
|
{:else if isSelected == false}
|
||||||
|
<Icon name="square"/>
|
||||||
|
{:else}
|
||||||
|
<Icon name="plus-square-dotted" />
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
<Tooltip
|
||||||
|
target={`${job.cluster}-${job.jobId}-select`}
|
||||||
|
placement="left">
|
||||||
|
{ 'Add or Remove Job to/from Comparison Selection' }
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
<Button id={`${job.cluster}-${job.jobId}-clipboard`} outline color="secondary" size="sm" on:click={clipJobId(job.jobId)} >
|
<Button id={`${job.cluster}-${job.jobId}-clipboard`} outline color="secondary" size="sm" on:click={clipJobId(job.jobId)} >
|
||||||
{#if displayCheck}
|
{#if displayCheck}
|
||||||
<Icon name="clipboard2-check-fill"/>
|
<Icon name="clipboard2-check-fill"/>
|
||||||
@ -89,6 +111,7 @@
|
|||||||
{ displayCheck ? 'Copied!' : 'Copy Job ID to Clipboard' }
|
{ displayCheck ? 'Copied!' : 'Copy Job ID to Clipboard' }
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
|
</span>
|
||||||
{#if job.metaData?.jobName}
|
{#if job.metaData?.jobName}
|
||||||
{#if job.metaData?.jobName.length <= 25}
|
{#if job.metaData?.jobName.length <= 25}
|
||||||
<div>{job.metaData.jobName}</div>
|
<div>{job.metaData.jobName}</div>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||||
import { getContext } from "svelte";
|
import { getContext, createEventDispatcher } from "svelte";
|
||||||
import { Card, Spinner } from "@sveltestrap/sveltestrap";
|
import { Card, Spinner } from "@sveltestrap/sveltestrap";
|
||||||
import { maxScope, checkMetricDisabled } from "../utils.js";
|
import { maxScope, checkMetricDisabled } from "../utils.js";
|
||||||
import JobInfo from "./JobInfo.svelte";
|
import JobInfo from "./JobInfo.svelte";
|
||||||
@ -25,7 +25,9 @@
|
|||||||
export let plotHeight = 275;
|
export let plotHeight = 275;
|
||||||
export let showFootprint;
|
export let showFootprint;
|
||||||
export let triggerMetricRefresh = false;
|
export let triggerMetricRefresh = false;
|
||||||
|
export let previousSelect = false;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
const resampleConfig = getContext("resampling") || null;
|
const resampleConfig = getContext("resampling") || null;
|
||||||
const resampleDefault = resampleConfig ? Math.max(...resampleConfig.resolutions) : 0;
|
const resampleDefault = resampleConfig ? Math.max(...resampleConfig.resolutions) : 0;
|
||||||
|
|
||||||
@ -39,6 +41,8 @@
|
|||||||
let zoomStates = {};
|
let zoomStates = {};
|
||||||
let thresholdStates = {};
|
let thresholdStates = {};
|
||||||
|
|
||||||
|
$: isSelected = previousSelect || null;
|
||||||
|
|
||||||
const cluster = getContext("clusters").find((c) => c.name == job.cluster);
|
const cluster = getContext("clusters").find((c) => c.name == job.cluster);
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const query = gql`
|
const query = gql`
|
||||||
@ -112,6 +116,12 @@
|
|||||||
refreshMetrics();
|
refreshMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: if (isSelected == true && previousSelect == false) {
|
||||||
|
dispatch("select-job", job.id)
|
||||||
|
} else if (isSelected == false && previousSelect == true) {
|
||||||
|
dispatch("unselect-job", job.id)
|
||||||
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
const selectScope = (jobMetrics) =>
|
const selectScope = (jobMetrics) =>
|
||||||
jobMetrics.reduce(
|
jobMetrics.reduce(
|
||||||
@ -152,7 +162,7 @@
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<JobInfo {job} />
|
<JobInfo {job} bind:isSelected showSelect/>
|
||||||
</td>
|
</td>
|
||||||
{#if job.monitoringStatus == 0 || job.monitoringStatus == 2}
|
{#if job.monitoringStatus == 0 || job.monitoringStatus == 2}
|
||||||
<td colspan={metrics.length}>
|
<td colspan={metrics.length}>
|
||||||
|
@ -308,7 +308,7 @@
|
|||||||
style="background-color: rgba(255, 255, 255, 1.0);" class="rounded"
|
style="background-color: rgba(255, 255, 255, 1.0);" class="rounded"
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<Card body color="warning" class="mx-4"
|
<Card body color="warning" class="mx-4 my-2"
|
||||||
>Cannot render plot: No series data returned for <code>{metric}</code></Card
|
>Cannot render plot: No series data returned for <code>{metric?metric:'job resources'}</code></Card
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -461,11 +461,11 @@ export function convert2uplot(canvasData, secondsToMinutes = false, secondsToHou
|
|||||||
} else { // Default -> Fill Histodata with zero values on unused value placing -> maybe allows zoom trigger as known
|
} else { // Default -> Fill Histodata with zero values on unused value placing -> maybe allows zoom trigger as known
|
||||||
if (secondsToHours) {
|
if (secondsToHours) {
|
||||||
let hours = cd.value / 3600
|
let hours = cd.value / 3600
|
||||||
console.log("x seconds to y hours", cd.value, hours)
|
// console.log("x seconds to y hours", cd.value, hours)
|
||||||
uplotData[0].push(hours)
|
uplotData[0].push(hours)
|
||||||
} else if (secondsToMinutes) {
|
} else if (secondsToMinutes) {
|
||||||
let minutes = cd.value / 60
|
let minutes = cd.value / 60
|
||||||
console.log("x seconds to y minutes", cd.value, minutes)
|
// console.log("x seconds to y minutes", cd.value, minutes)
|
||||||
uplotData[0].push(minutes)
|
uplotData[0].push(minutes)
|
||||||
} else {
|
} else {
|
||||||
uplotData[0].push(cd.value)
|
uplotData[0].push(cd.value)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user