From 897c6409df811ed7be1b2b9b3f0ecd2f2639c3ff Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 17 Jul 2023 17:38:08 +0200 Subject: [PATCH 01/13] Add Quotas --- src/components/CalculateQuota.js | 361 +++++++++++++++++++++++++++++++ src/components/Quotas.jsx | 357 ++++++++++++++++++++++++++++++ src/components/Usage.jsx | 98 +++++---- 3 files changed, 777 insertions(+), 39 deletions(-) create mode 100644 src/components/CalculateQuota.js create mode 100644 src/components/Quotas.jsx diff --git a/src/components/CalculateQuota.js b/src/components/CalculateQuota.js new file mode 100644 index 0000000..477dac7 --- /dev/null +++ b/src/components/CalculateQuota.js @@ -0,0 +1,361 @@ +function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput) { + const calcstartTime = calcstartTimeInput + const calcEndTime = calcEndTimeInput + + // extract the three different job types + const dataJobUsage = data['job_usage']==null?[]:data['job_usage']; + const dataPoolUsage = data['pool_usage']==null?[]:data['pool_usage']; + const dataHypercube = data['hypercube_job_usage']==null?[]:data['hypercube_job_usage']; + + // first extract all the pool infos, to later check if an individual job was part of a pool + let PoolLabels = dataPoolUsage.map(pool => pool['label']); + let poolInstances = dataPoolUsage.map(pool => pool['instance']['label']); + let poolOwners = dataPoolUsage.map(pool => pool['owner']); + + // only need the idle multiplier from the pools + // for the jobs use the multiplier from the jop instance + let PoolMultipliersIdle = dataPoolUsage.map(pool => pool['instance']['multiplier_idle']); + + // contains for every pool a list of the times of every worker + // each element of the individual lists is a dictionary + let PoolTimes = dataPoolUsage.map(pool => pool['times']); + + let CommentPool = Array(PoolLabels.length).fill('This is the idle pool time. '); + let FailsPool = Array(PoolLabels.length).fill(0); + let isIdlePool = Array(PoolLabels.length).fill(true); + + // check if workers failed, for this collect all worker_id's for each pool + // if there are multiple entries for the same id, only keep the last one + // the number of fails is saved as a comment and as an actual number, + // to later easily compute the total number of fails + PoolTimes.forEach(function (pool, i) { + // extract the worker_id for each timeframe + let workers = pool.map(time => time['worker_id']); + const workersWithoutDuplicates = [...new Set(workers)]; + workersWithoutDuplicates.forEach(function(worker) { + // need to save count for the comment, because it changes after the duplicates are deleted + const count = workers.filter(el => el === worker).length; + // if a worker failed set the start time of the last occurence of this worker to the first start time + if (count > 1) { + // get indices of the same worker + let indices = []; + for (let idx = 0; idx < workers.length; idx++) { + if (workers[idx] === worker) { + indices.push(idx); + } + } + // change the start time of the current worker to the start of it's first occurence + PoolTimes[i][indices[indices.length - 1]].start = PoolTimes[i][indices[0]].start; + // Now remove all other occurences from PoolTimes + indices = indices.slice(0, -1); + // remove all elements in PoolTimes and pool_workers + PoolTimes[i] = PoolTimes[i].filter(function(time, idx) { + return !indices.includes(idx); + }); + workers = workers.filter(function(worker_id, idx) { + return !indices.includes(idx); + }); + + CommentPool[i] = CommentPool[i].concat(`${worker} failed ${count - 1} times. `); + FailsPool[i] = count - 1 + } + }); + }); + + // compute the whole time workers existed on the pool (and didn't fail because those workers are already removed) + PoolTimes.forEach(function (pool, i) { + let totalWorkerTime = 0; + pool.forEach(function(times) { + let startTime = new Date(times['start']); + let finishTime = calcEndTime; + // check if the worker is still running + if (times['finish'] != null) { + finishTime = new Date(times['finish']); + } + else if (dataPoolUsage[i]['deleted_at'] != null){ + finishTime = new Date( dataPoolUsage[i]['deleted_at']); + CommentPool[i] = CommentPool[i].concat('There is no finish time, but the job was finished!!'); + } + else { + finishTime = calcEndTime; + CommentPool[i] = CommentPool[i].concat(`A worker on ${PoolLabels[i]} is still running. `); + } + // only include the parts of the worker times that are in the given timeframe + if (startTime >= calcstartTime && finishTime <= calcEndTime) { + totalWorkerTime += finishTime - startTime; + } else if (calcstartTime <= finishTime && finishTime <= calcEndTime) { + totalWorkerTime += finishTime - calcstartTime; + } else if (calcstartTime <= startTime && startTime <= calcEndTime) { + totalWorkerTime += calcEndTime - startTime; + } else if (startTime <= calcstartTime && calcEndTime <= finishTime) { + totalWorkerTime += calcEndTime - calcstartTime; + } + }); + + // if no worker of the pool was in the timeframe the timedelta is zero! + PoolTimes[i] = totalWorkerTime + }); + + // only needed to later exclude pools, that didn't had a worker in the timeframe + // if timedelta = 0 this will return False, else set to True + let includedPools = PoolTimes.map(item => Boolean(item)); + + // collect the instances and the corresponding multipliers + let instances = dataJobUsage.map(row => row['labels']['instance']); + + let multipliers = dataJobUsage.map(row => row['labels']['multiplier']); + let times = dataJobUsage.map(row => row['times']); + + let users = dataJobUsage.map(row => row['username']); + + // used to later only take the instances for jobs that are in the timeframe + let includedItems = Array(dataJobUsage.length).fill(false); + + let comments = Array(dataJobUsage.length).fill(''); + let fails = Array(dataJobUsage.length).fill(0); + let isIdle = Array(dataJobUsage.length).fill(false); + + // extract the hypercube informations an add them to the job list + let hypercubeInstances = []; + let hypercubeMultipliers = []; + let hypercubeTimes = []; + let hypercubeUsers = []; + + // go over every hypercube in the data + dataHypercube.forEach(function(hypercube) { + // save the label and mutliplier of the current hypercupe to add them to the job list + let currentInstance = hypercube['labels']['instance']; + let currentMultiplier = hypercube['labels']['multiplier']; + let currentUser = hypercube['username']; + // go over every job of each hypercube to collect the times + // and append the instances/multipliers/time list, + // to later just concatenate this with the other job list + // this way also the check if its a pool job will be performed + hypercube['jobs'].forEach(function(job) { + hypercubeInstances.push(currentInstance); + hypercubeMultipliers.push(currentMultiplier); + hypercubeTimes.push(job['times']); + hypercubeUsers.push(currentUser); + }); + }); + + const hypercubeIncludedItems = Array(hypercubeInstances.length).fill(false); + const hypercubeComments = Array(hypercubeInstances.length).fill('This job was part of a hypercube. '); + const hypercubeFails = Array(hypercubeInstances.length).fill(0); + const hypercubeIsIdle = Array(hypercubeInstances.length).fill(false); + + instances = instances.concat(hypercubeInstances); + multipliers = multipliers.concat(hypercubeMultipliers); + times = times.concat(hypercubeTimes); + includedItems = includedItems.concat(hypercubeIncludedItems); + comments = comments.concat(hypercubeComments); + fails = fails.concat(hypercubeFails); + users = users.concat(hypercubeUsers); + isIdle = isIdle.concat(hypercubeIsIdle); + + // will also report the corresponding given pool_label + // is None for jobs not run on a pool + const jobPoolLabels = Array(instances.length).fill(null); + + // needed to later check to which pool a pool_job corresponds + let jobStartTimes = dataJobUsage.map(job => new Date(job['submitted'])); + const jobHypercubeStartTimes = dataHypercube.map(job => new Date(job['submitted'])); + jobStartTimes = jobStartTimes.concat(jobHypercubeStartTimes); + + // check which jobs are in the timeframe and compute their run time + times.forEach(function(time, i) { + if (time.length !== 0){ + // always take the last element, so it also works if the job crashed before + let startTime = new Date(time[time.length - 1]['start']); + // check if the job is still running + let finishTime = calcEndTime; + if (time[time.length - 1]['finish']) { + finishTime = new Date(time[time.length - 1]['finish']); + } + else if(dataJobUsage[i]['finished'] != null){ + finishTime = new Date(dataJobUsage[i]['finished']) + comments[i] = comments[i].concat('There is no finish time, but the job was finished!!') + } + else{ + finishTime = calcEndTime; + comments[i] = comments[i].concat('The job is still running. '); + } + // check if the whole job was run in the timeframe + if (startTime >= calcstartTime && finishTime <= calcEndTime) { + times[i] = finishTime - startTime; + includedItems[i] = true; + } + // or if it finished during the timeframe + else if (calcstartTime <= finishTime && finishTime <= calcEndTime) { + times[i] = finishTime - calcstartTime; + includedItems[i] = true; + comments[i] += 'Finished in the given timeframe, but started earlier. '; + } + // or finally if it started during the timeframe + else if (calcstartTime <= startTime && startTime <= calcEndTime) { + times[i] = calcEndTime - startTime; + includedItems[i] = true; + comments[i] += 'Started in the given timeframe, but is not finished yet. '; + } + // or if it ran over the whole timeframe + else if (startTime <= calcstartTime && calcEndTime <= finishTime) { + times[i] = calcEndTime - calcstartTime; + includedItems[i] = true; + comments[i] += 'Job ran longer than the timeframe. '; + } + // no else case needed! All other times get discarded + // add debugg option: + // if debug=True this case should not happen, because no job outside of the timeframe + // should be in the json object + else { + console.error('Found job outside of the timeframe, even though this should not happen!'); + } + + fails[i] = time.length - 1; + if (time.length > 1){ + comments[i] = comments[i].concat(`The job failed ${time.length - 1} times. `); + } + } + }); + + // get existing times for the pools + const poolCreated = dataPoolUsage.map(pool => new Date(pool['created_at'])); + const poolDeleted = dataPoolUsage.map(pool => pool['deleted_at']); + + poolDeleted.forEach(function(time, i){ + if (time == null){ + poolDeleted[i] = calcEndTime; + } + else{ + poolDeleted[i] = new Date(poolDeleted[i]); + } + }); + + // substract the jobs corresponding to a pool from the PoolTimes to get the idle time + times.forEach(function(time, i) { + if (includedItems[i]) { + if (PoolLabels.includes(instances[i])){ + // get indices of the pool_label + let indices = []; + for (let idx = 0; idx < PoolLabels.length; idx++) { + if (instances[i] === PoolLabels[idx]) { + indices.push(idx); + } + } + indices.forEach(function(index) { + // find the corresponding pool, if no pool fits the job is processed as a normal job + if (poolCreated[index] <= jobStartTimes[i] && jobStartTimes[i] <= poolDeleted[index]) { + PoolTimes[index] -= times[i]; + comments[i] += 'This job ran on a pool. '; + jobPoolLabels[i] = instances[i]; + instances[i] = poolInstances[index]; + } + }); + } + } + }); + + // add the pool times at the end of the data + instances = instances.concat(poolInstances); + poolInstances = jobPoolLabels.concat(PoolLabels); + multipliers = multipliers.concat(PoolMultipliersIdle); + times = times.concat(PoolTimes); + comments = comments.concat(CommentPool); + fails = fails.concat(FailsPool); + includedItems = includedItems.concat(includedPools); + users = users.concat(poolOwners); + isIdle = isIdle.concat(isIdlePool); + + // only take the elements corresponding to jobs/pools in the timeframe + instances = instances.filter((_, i) => includedItems[i]); + poolInstances = poolInstances.filter((_, i) => includedItems[i]); + multipliers = multipliers.filter((_, i) => includedItems[i]); + times = times.filter((_, i) => includedItems[i]); + comments = comments.filter((_, i) => includedItems[i]); + fails = fails.filter((_, i) => includedItems[i]); + users = users.filter((_, i) => includedItems[i]); + isIdle = isIdle.filter((_, i) => includedItems[i]); + + const calcTimes = { + 'users': users, + 'instances': instances, + 'pool_labels': poolInstances, + 'multipliers': multipliers, + 'times': times, + 'comments': comments, + 'fails': fails, + 'is_idle': isIdle + } + + // split into jobs and idle pool time + let calcTimesJobs = { + 'users': [], + 'instances': [], + 'pool_labels': [], + 'multipliers': [], + 'times': [], + 'comments': [], + 'fails': [] + } + + let calcTimesPools = { + 'users': [], + 'instances': [], + 'pool_labels': [], + 'multipliers': [], + 'times': [], + 'comments': [], + 'fails': [] + } + + calcTimes.is_idle.forEach(function (elem, i) { + if (!elem) { + calcTimesJobs.users.push(calcTimes.users[i]) + calcTimesJobs.instances.push(calcTimes.instances[i]) + calcTimesJobs.pool_labels.push(calcTimes.pool_labels[i]) + calcTimesJobs.multipliers.push(calcTimes.multipliers[i]) + calcTimesJobs.times.push(calcTimes.times[i]) + calcTimesJobs.comments.push(calcTimes.comments[i]) + calcTimesJobs.fails.push(calcTimes.fails[i]) + } + else { + calcTimesPools.users.push(calcTimes.users[i]) + calcTimesPools.instances.push(calcTimes.instances[i]) + calcTimesPools.pool_labels.push(calcTimes.pool_labels[i]) + calcTimesPools.multipliers.push(calcTimes.multipliers[i]) + calcTimesPools.times.push(calcTimes.times[i]) + calcTimesPools.comments.push(calcTimes.comments[i]) + calcTimesPools.fails.push(calcTimes.fails[i]) + } + }) + + const uniqueId = Array.from(Array(calcTimes.instances.length).keys()).map(el => `el_${el}`); + + let ungroupedDataJobs = [] + let ungroupedDataPools = [] + + calcTimesJobs['instances'].forEach(function (elem, i) { + ungroupedDataJobs.push({ uniqueId: uniqueId[i], user: calcTimesJobs.users[i], instances: elem, + pool_labels: calcTimesJobs.pool_labels[i], multipliers: calcTimesJobs.multipliers[i].toString(), times: calcTimesJobs.times[i], + comments: calcTimesJobs.comments[i], fails: calcTimesJobs.fails[i], jobs: '1'}) + }); + + + + calcTimesPools['instances'].forEach(function (elem, i) { + ungroupedDataPools.push({ unique_id: uniqueId[i], user: calcTimesPools.users[i], instances: elem, + pool_labels: calcTimesPools.pool_labels[i], multipliers: calcTimesPools.multipliers[i].toString(), times: calcTimesPools.times[i], + comments: calcTimesPools.comments[i], fails: calcTimesPools.fails[i], jobs: '1'}) + }); + + const result = { + 'data_jobs': ungroupedDataJobs, + 'data_pools': ungroupedDataPools + } + + return ( + result + ) +} + +export default getComputationTimes diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx new file mode 100644 index 0000000..0b1dcd3 --- /dev/null +++ b/src/components/Quotas.jsx @@ -0,0 +1,357 @@ +// not here, no default set +import { testData } from './data.jsx'; +import { useEffect, useState } from 'react'; +// can rename, because it takes the default export +import computeTimes from './CalculateQuota.js' +import Table from './Table.jsx' +import Select from 'react-select'; + + +const Quotas = ({testData, calcStartDate, calcEndTime}) => { +/* + const data = test_data['test_hypercube_with_pool_and_job']; + const calcStartDate = "2021-08-03T17:10:15.000000+00:00"; + const calcEndTime = "2021-08-05T17:10:15.000000+00:00"; + + let test_tableData = [{ unique_id: 'test', instances: 'bla', pool_labels: 'jaja', multipliers: 1, times: 17263716, comments: 'test123', fails: 0 }]; +*/ + + // const data = test_data['real_test_data']; +// const data = testData +// const calcStartDate = new Date("2021-01-04T17:10:15.000000+00:00"); +// const calcEndTime = new Date("2023-03-31T17:10:15.000000+00:00"); + +const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) +const [ungroupedDataJobs, setUngroupedDataJobs] = useState(dataTmp.data_jobs); +const [ungroupedDataPools, setUngroupedDataPools] = useState(dataTmp.data_pools); + +useEffect(() => { + const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) + setUngroupedDataJobs(dataTmp.data_jobs) + setUngroupedDataPools(dataTmp.data_pools) + }, [testData, calcStartDate, calcEndTime]) + + +// const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) +// ungroupedDataJobs = dataTmp.data_jobs +// ungroupedDataPools = dataTmp.data_pools + + const displayFieldUngrouped = [ + // { + // field: "unique_id", + // column: "id", + // sorter: "alphabetical", + // displayer: String + // }, + { + field: "user", + column: "User", + sorter: "alphabetical", + displayer: String + }, + { + field: "times", + column: "Solve Time", + sorter: "numerical", + displayer: formatTime + }, + { + field: "fails", + column: "Number Fails", + sorter: "numerical", + displayer: Number + }, + { + field: "jobs", + column: "Number Jobs", + sorter: "alphabetical", + displayer: String + }, + { + field: "instances", + column: "Instance", + sorter: "alphabetical", + displayer: String + }, + { + field: "pool_labels", + column: "Pool Label", + sorter: "alphabetical", + displayer: (pool_label) => pool_label == null? '-': pool_label + }, + { + field: "multipliers", + column: "Multiplier", + sorter: "alphabetical", + displayer: String + } + ] + + const displayFieldUser = [ + { + field: "user", + column: "User", + sorter: "alphabetical", + displayer: String + }, + { + field: "times", + column: "Solve Time", + sorter: "numerical", + displayer: formatTime + }, + { + field: "fails", + column: "Number Fails", + sorter: "numerical", + displayer: Number + }, + { + field: "jobs", + column: "Number Jobs", + sorter: "alphabetical", + displayer: String + } + ] + + const displayFieldInstance = [ + { + field: "instances", + column: "Instances", + sorter: "alphabetical", + displayer: String + }, + { + field: "times", + column: "Solve Time", + sorter: "numerical", + displayer: formatTime + }, + { + field: "fails", + column: "Number Fails", + sorter: "numerical", + displayer: Number + }, + { + field: "jobs", + column: "Number Jobs", + sorter: "alphabetical", + displayer: String + } + ] + + const displayFieldPoolLabel = [ + { + field: "pool_labels", + column: "Pool Labels", + sorter: "alphabetical", + displayer: String + }, + { + field: "times", + column: "Solve Time", + sorter: "numerical", + displayer: formatTime + }, + { + field: "fails", + column: "Number Fails", + sorter: "numerical", + displayer: Number + }, + { + field: "jobs", + column: "Number Jobs", + sorter: "alphabetical", + displayer: String + } + ] + const [displayFieldsDisaggregated, setDisplayFieldsDisaggregated] = useState(displayFieldUngrouped); + + const availableAggregateTypes = [{value: '_', label: '_'}, {value: "username", label: 'User'}, {value: "instance", label: 'Instance'}, {value: "pool_label", label: 'Pool_label'}] + + const [selectedAggregateType, setSelectedAggregateType] = useState('_') + const [totalUsage, setTotalUsage] = useState(0); + const [tableDataJobs, setTableDataJobs] = useState([]) + const [tableDataPools, setTableDataPools] = useState([]) + + let sumTmp = 0 + sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + + useEffect(()=> { + if (selectedAggregateType === '_') { + // don't display null for pool_lable + // only needed in the unfilterd case, because the others don't show pool_label + console.log(ungroupedDataJobs) + console.log('2222') + // let ungroupedDataJobsTmp = ungroupedDataJobs + // ungroupedDataJobsTmp.forEach(function (elem, i) { + // if (elem.pool_labels == null) { + // ungroupedDataJobsTmp[i].pool_labels = '-' + // } + // }) + setTableDataJobs(ungroupedDataJobs) + setTableDataPools(ungroupedDataPools) + setTotalUsage(sumTmp) + console.log(ungroupedDataJobs) + setDisplayFieldsDisaggregated(displayFieldUngrouped) + } else if (selectedAggregateType === 'username') { + setTableDataJobs(GroupByUser(ungroupedDataJobs)) + setTableDataPools(GroupByUser(ungroupedDataPools)) + setTotalUsage(sumTmp) + setDisplayFieldsDisaggregated(displayFieldUser) + } else if (selectedAggregateType === 'instance') { + setTableDataJobs(GroupByInstance(ungroupedDataJobs)) + setTableDataPools(GroupByInstance(ungroupedDataPools)) + setTotalUsage(sumTmp) + setDisplayFieldsDisaggregated(displayFieldInstance) + } else if (selectedAggregateType === 'pool_label') { + setTableDataJobs(GroupByPoolLabel(ungroupedDataJobs)) + setTableDataPools(GroupByPoolLabel(ungroupedDataPools)) + sumTmp = ungroupedDataJobs.filter(el => el.pool_label != null).reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + setTotalUsage(sumTmp) + setDisplayFieldsDisaggregated(displayFieldPoolLabel) + } + + }, [selectedAggregateType, ungroupedDataJobs, ungroupedDataPools]) + + return ( +
+
+ + type.value === metric)[0]} + isSearchable={true} + onChange={selected => setMetric(selected.value)} + options={availableMetrics} + /> +
+

Total: {new Intl.NumberFormat('en-US', { style: 'decimal' }).format(totalUsage)} {metric}

Jobs

{

Idle Pool Times

Date: Mon, 24 Jul 2023 13:55:25 +0200 Subject: [PATCH 05/13] add change of metric --- src/components/Quotas.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 9675b8d..82ee3b6 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -35,6 +35,13 @@ useEffect(() => { const [metric, setMetric] = useState('mults') + useEffect(() => { + if (metric === 'mults') { + setTotalUsage(totalUsage * 3600) + } else if (metric === 'multh') { + setTotalUsage(totalUsage / 3600) + } + }, [metric]) const displayFieldUngrouped = [ // { @@ -119,7 +126,7 @@ useEffect(() => { const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['token,is_hypercube'].includes(el.field)) setTableDataJobs(ungroupedDataJobs) setTableDataPools(ungroupedDataPools) - setTotalUsage(sumTmp) + setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) setDisplayFieldsJobs(displayFieldUngrouped) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'username') { @@ -127,7 +134,7 @@ useEffect(() => { const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['instances', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByUser(ungroupedDataJobs)) setTableDataPools(GroupByUser(ungroupedDataPools)) - setTotalUsage(sumTmp) + setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'instance') { @@ -135,7 +142,7 @@ useEffect(() => { const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByInstance(ungroupedDataJobs)) setTableDataPools(GroupByInstance(ungroupedDataPools)) - setTotalUsage(sumTmp) + setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'pool_label') { @@ -145,7 +152,7 @@ useEffect(() => { setTableDataPools(GroupByPoolLabel(ungroupedDataPools)) sumTmp = ungroupedDataJobs.filter(el => el.pool_label != null).reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) - setTotalUsage(sumTmp) + setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } From db9da2830c925c0047d2935d362d53b6eafcface Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 10:37:39 +0200 Subject: [PATCH 06/13] correct pool username retrieval --- src/components/CalculateQuota.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CalculateQuota.js b/src/components/CalculateQuota.js index b8af497..3610c68 100644 --- a/src/components/CalculateQuota.js +++ b/src/components/CalculateQuota.js @@ -10,7 +10,7 @@ function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput) { // first extract all the pool infos, to later check if an individual job was part of a pool let PoolLabels = dataPoolUsage.map(pool => pool['label']); let poolInstances = dataPoolUsage.map(pool => pool['instance']['label']); - let poolOwners = dataPoolUsage.map(pool => pool['owner']); + let poolOwners = dataPoolUsage.map(pool => pool['owner']['username']); // only need the idle multiplier from the pools // for the jobs use the multiplier from the jop instance From c24fc4f1ee893f5677ae99a60a336a389830fffb Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 10:38:10 +0200 Subject: [PATCH 07/13] add pie charts --- src/components/Quotas.jsx | 464 +++++++++++++++++++++++--------------- 1 file changed, 277 insertions(+), 187 deletions(-) diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 82ee3b6..1489524 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -1,49 +1,101 @@ // not here, no default set import { testData } from './data.jsx'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { Chart as ChartJS, ArcElement, Legend, Tooltip } from 'chart.js'; +import { Pie } from 'react-chartjs-2'; +import 'chartjs-adapter-date-fns'; // can rename, because it takes the default export import computeTimes from './CalculateQuota.js' import Table from './Table.jsx' -import Select from 'react-select'; -import {Link} from "react-router-dom"; +import Select from 'react-select'; +import { Link } from "react-router-dom"; -const Quotas = ({testData, calcStartDate, calcEndTime}) => { -/* - const data = test_data['test_hypercube_with_pool_and_job']; - const calcStartDate = "2021-08-03T17:10:15.000000+00:00"; - const calcEndTime = "2021-08-05T17:10:15.000000+00:00"; +ChartJS.register(ArcElement, Tooltip, Legend); - let test_tableData = [{ unique_id: 'test', instances: 'bla', pool_labels: 'jaja', multipliers: 1, times: 17263716, comments: 'test123', fails: 0 }]; -*/ + + +const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { + /* + const data = test_data['test_hypercube_with_pool_and_job']; + const calcStartDate = "2021-08-03T17:10:15.000000+00:00"; + const calcEndTime = "2021-08-05T17:10:15.000000+00:00"; + + let test_tableData = [{ unique_id: 'test', instances: 'bla', pool_labels: 'jaja', multipliers: 1, times: 17263716, comments: 'test123', fails: 0 }]; + */ // const data = test_data['real_test_data']; -// const data = testData -// const calcStartDate = new Date("2021-01-04T17:10:15.000000+00:00"); -// const calcEndTime = new Date("2023-03-31T17:10:15.000000+00:00"); + // const data = testData + // const calcStartDate = new Date("2021-01-04T17:10:15.000000+00:00"); + // const calcEndTime = new Date("2023-03-31T17:10:15.000000+00:00"); -const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) -const [ungroupedDataJobs, setUngroupedDataJobs] = useState(dataTmp.data_jobs); -const [ungroupedDataPools, setUngroupedDataPools] = useState(dataTmp.data_pools); + const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) + const [ungroupedDataJobs, setUngroupedDataJobs] = useState(dataTmp.data_jobs); + const [ungroupedDataPools, setUngroupedDataPools] = useState(dataTmp.data_pools); -useEffect(() => { + useEffect(() => { const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) setUngroupedDataJobs(dataTmp.data_jobs) setUngroupedDataPools(dataTmp.data_pools) - }, [testData, calcStartDate, calcEndTime]) + }, [testData, calcStartDate, calcEndTime]) + + + function getChartData(label, ungroupedData) { + let groupedData = [] + let labels = [] + let times = [] + if (label === 'usernames') { + groupedData = GroupByUser(ungroupedData); + labels = groupedData.map(elem => elem.user); + times = groupedData.map(elem => elem.times); + } else if (label === 'instances') { + groupedData = GroupByInstance(ungroupedData); + labels = groupedData.map(elem => elem.instances); + times = groupedData.map(elem => elem.times); + } else if (label === 'pool_labels') { + groupedData = GroupByPoolLabel(ungroupedData); + labels = groupedData.map(elem => elem.pool_labels); + times = groupedData.map(elem => elem.times); + } + + const cutOff = 20; + if (labels.length > cutOff) { + setTruncateWarning(current => `${current} Only the ${cutOff} most used ${label} displayed. `) + const labelTimePairs = labels.map((label, index) => ({ label, time: times[index] })); + + // Sort the array of objects based on decreasing time + labelTimePairs.sort((a, b) => b.time - a.time); + // Extract the sorted labels and times separately + labels = labelTimePairs.map(pair => pair.label); + times = labelTimePairs.map(pair => pair.time); + labels = labels.slice(0,cutOff); + times = times.slice(0,cutOff) + } - const [metric, setMetric] = useState('mults') - useEffect(() => { - if (metric === 'mults') { - setTotalUsage(totalUsage * 3600) - } else if (metric === 'multh') { - setTotalUsage(totalUsage / 3600) + return { + labels: labels, + datasets: [ + { + label: '# of Votes', + data: times, + backgroundColor: ["rgba(31,120,180,0.2)", "rgba(51,160,44,0.2)", + "rgba(227,26,28,0.2)", "rgba(255,127,0,0.2)", + "rgba(106,61,154,0.2)", "rgba(177,89,40,0.2)", + "rgba(249,185,183,0.2)", "rgba(173,169,183,0.2)", + "rgba(102,16,31,0.2)", "rgba(196,90,179,0.2)", + "rgba(27,231,255,0.2)", "rgba(76,159,112,0.2)", + "rgba(240,247,87,0.2)", "rgba(158,109,66,0.2)", + "rgba(8,103,136,0.2)", "rgba(224,202,60,0.2)", + "rgba(186,151,144,0.2)", "rgba(235,69,17,0.2)", + "rgba(155,93,229,0.2)", "rgba(71,250,26,0.2)"], + }, + ], + } } - }, [metric]) - const displayFieldUngrouped = [ + const displayFieldUngrouped = useRef([ // { // field: "unique_id", // column: "id", @@ -51,10 +103,10 @@ useEffect(() => { // displayer: String // }, { - field: "user", + field: "user, unique_id", column: "User", sorter: "alphabetical", - displayer: String + displayer: (user, _) => user }, { field: "instances", @@ -66,26 +118,26 @@ useEffect(() => { field: "pool_labels", column: "Pool Label", sorter: "alphabetical", - displayer: (pool_label) => pool_label == null? '-': pool_label + displayer: (pool_label) => pool_label == null ? '-' : pool_label }, { - field: "times", - column: "Solve Time", - sorter: "numerical", - displayer: formatTime - }, - { - field: "fails", - column: "Number Fails", - sorter: "numerical", - displayer: Number - }, - { - field: "jobs", - column: "Number Jobs", - sorter: "alphabetical", - displayer: String - }, + field: "times", + column: "Solve Time", + sorter: "numerical", + displayer: formatTime + }, + { + field: "fails", + column: "Number Fails", + sorter: "numerical", + displayer: Number + }, + { + field: "jobs", + column: "Number Jobs", + sorter: "alphabetical", + displayer: String + }, { field: "multipliers", column: "Multiplier", @@ -93,117 +145,150 @@ useEffect(() => { displayer: String }, { - field: "token,is_hypercube", - column: "Job token", - displayer: (name, job_count) => <> - {job_count === true ? {name} - - HC - : - {name}} - + field: "token,is_hypercube", + column: "Job token", + displayer: (name, job_count) => <> + {job_count === true ? {name} + + HC + : + {name}} + } - ] + ]) - const [displayFieldsJobs, setDisplayFieldsJobs] = useState(displayFieldUngrouped); - const [displayFieldsPools, setDisplayFieldsPools] = useState(displayFieldUngrouped); + const [displayFieldsJobs, setDisplayFieldsJobs] = useState(displayFieldUngrouped.current); + const [displayFieldsPools, setDisplayFieldsPools] = useState(displayFieldUngrouped.current); - const availableAggregateTypes = [{value: '_', label: '_'}, {value: "username", label: 'User'}, {value: "instance", label: 'Instance'}, {value: "pool_label", label: 'Pool_label'}] - const availableMetrics = [{value: 'mults', label: 'mults'}, {value: 'multh', label: 'multh'}] + const availableAggregateTypes = [{ value: '_', label: '_' }, { value: "username", label: 'User' }, { value: "instance", label: 'Instance' }, { value: "pool_label", label: 'Pool_label' }] const [selectedAggregateType, setSelectedAggregateType] = useState('_') const [totalUsage, setTotalUsage] = useState(0); const [tableDataJobs, setTableDataJobs] = useState([]) const [tableDataPools, setTableDataPools] = useState([]) + const [userChartData, setUserChartData] = useState({ labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]}) + const [instanceChartData, setInstanceChartData] = useState({ labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]}) + const [poolLabelChartData, setPoolLabelChartData] = useState({ labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]}) - let sumTmp = 0 - sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) - sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + const [truncateWarning, setTruncateWarning] = useState([]) - useEffect(()=> { + useEffect(() => { if (selectedAggregateType === '_') { - const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['token,is_hypercube'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['token,is_hypercube'].includes(el.field)) setTableDataJobs(ungroupedDataJobs) setTableDataPools(ungroupedDataPools) - setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) - setDisplayFieldsJobs(displayFieldUngrouped) + let sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + setTotalUsage(sumTmp) + setDisplayFieldsJobs(displayFieldUngrouped.current) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'username') { - const displayFieldsTmpJob = displayFieldUngrouped.filter(el => !['instances', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) - const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['instances', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['instances', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['instances', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByUser(ungroupedDataJobs)) setTableDataPools(GroupByUser(ungroupedDataPools)) - setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) + let sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + setTotalUsage(sumTmp) setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) - } else if (selectedAggregateType === 'instance') { - const displayFieldsTmpJob = displayFieldUngrouped.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) - const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) + } else if (selectedAggregateType === 'instance') { + const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByInstance(ungroupedDataJobs)) setTableDataPools(GroupByInstance(ungroupedDataPools)) - setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) + let sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + setTotalUsage(sumTmp) setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'pool_label') { - const displayFieldsTmpJob = displayFieldUngrouped.filter(el => !['instances', 'user', 'multipliers', 'token,is_hypercube'].includes(el.field)) - const displayFieldsTmpPool = displayFieldUngrouped.filter(el => !['instances', 'user', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['instances', 'user', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['instances', 'user', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByPoolLabel(ungroupedDataJobs)) setTableDataPools(GroupByPoolLabel(ungroupedDataPools)) - sumTmp = ungroupedDataJobs.filter(el => el.pool_label != null).reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) - sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) - setTotalUsage(metric === 'mults'? sumTmp: sumTmp/3600) + let sumTmp2 = ungroupedDataJobs.filter(el => el.pool_label != null).reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + sumTmp2 += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) + setTotalUsage(sumTmp2) setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } - - }, [selectedAggregateType, ungroupedDataJobs, ungroupedDataPools]) + + + }, [quotaUnit, selectedAggregateType, ungroupedDataJobs, ungroupedDataPools, displayFieldUngrouped]) + + useEffect(() => { + setTruncateWarning('') + let chartDataTmp = getChartData('usernames', ungroupedDataJobs) + if (chartDataTmp.labels.length === 0) { + chartDataTmp = { labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]} + } + setUserChartData(chartDataTmp) + chartDataTmp = getChartData('instances', ungroupedDataJobs) + if (chartDataTmp.labels.length === 0) { + chartDataTmp = { labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]} + } + setInstanceChartData(chartDataTmp) + chartDataTmp = getChartData('pool_labels', ungroupedDataJobs.concat(ungroupedDataPools)) + if (chartDataTmp.labels.length === 0) { + chartDataTmp = { labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]} + } + setPoolLabelChartData(chartDataTmp) + + }, [ungroupedDataJobs, ungroupedDataPools]) return (
- - type.value === selectedAggregateType)[0]} + isSearchable={true} + onChange={selected => setSelectedAggregateType(selected.value)} + options={availableAggregateTypes} + />
-
- -
+

Idle Pool Times

+
+ -

Total: {new Intl.NumberFormat('en-US', { style: 'decimal' }).format(totalUsage)} {metric}

-

Jobs

-
-

Idle Pool Times

-
); } @@ -226,86 +311,91 @@ function formatTime(milliseconds) { function GroupByUser(ungroupedData) { - let allUsers = ungroupedData.map(elem => elem.user); + let allUsers = ungroupedData.map(elem => elem.user); - let setOfAllUsers = [...new Set(allUsers)]; + let setOfAllUsers = [...new Set(allUsers)]; - let jobsPerUser = Array(setOfAllUsers.length).fill(0); - let FailsPerUser = Array(setOfAllUsers.length).fill(0); - let TimesPerUser = Array(setOfAllUsers.length).fill(0); - - allUsers.forEach(function (user, i) { - jobsPerUser[setOfAllUsers.indexOf(user)] += 1 - FailsPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].fails - TimesPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].times - }); - - let groupedUserData = [] - - // when grouped by user: 'user', 'pool_label' and 'multiplier' can't be shown anymore - setOfAllUsers.forEach(function (elem, i) { - groupedUserData.push({ unique_id: ungroupedData[i].unique_id, user: elem, - times: TimesPerUser[i], fails: FailsPerUser[i], jobs: jobsPerUser[i]}) - }); - - return groupedUserData + let jobsPerUser = Array(setOfAllUsers.length).fill(0); + let FailsPerUser = Array(setOfAllUsers.length).fill(0); + let TimesPerUser = Array(setOfAllUsers.length).fill(0); + + allUsers.forEach(function (user, i) { + jobsPerUser[setOfAllUsers.indexOf(user)] += 1 + FailsPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].fails + TimesPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].times + }); + + let groupedUserData = [] + + // when grouped by user: 'user', 'pool_label' and 'multiplier' can't be shown anymore + setOfAllUsers.forEach(function (elem, i) { + groupedUserData.push({ + unique_id: ungroupedData[i].unique_id, user: elem, + times: TimesPerUser[i], fails: FailsPerUser[i], jobs: jobsPerUser[i] + }) + }); + + return groupedUserData } function GroupByInstance(ungroupedData) { - let allInstances = ungroupedData.map(elem => elem.instances); + let allInstances = ungroupedData.map(elem => elem.instances); - let setOfAllInstances = [...new Set(allInstances)]; + let setOfAllInstances = [...new Set(allInstances)]; - let jobsPerInstances = Array(setOfAllInstances.length).fill(0); - let failsPerInstances = Array(setOfAllInstances.length).fill(0); - let timesPerInstances = Array(setOfAllInstances.length).fill(0); - - allInstances.forEach(function (user, i) { - jobsPerInstances[setOfAllInstances.indexOf(user)] += 1 - failsPerInstances[setOfAllInstances.indexOf(user)] += ungroupedData[i].fails - timesPerInstances[setOfAllInstances.indexOf(user)] += ungroupedData[i].times - }); - - let groupedInstanceData = [] - - // when grouped by user: 'user', 'pool_label' and 'multiplier' can't be shown anymore - setOfAllInstances.forEach(function (elem, i) { - groupedInstanceData.push({ unique_id: ungroupedData[i].unique_id, instances: elem, - times: timesPerInstances[i], fails: failsPerInstances[i], jobs: jobsPerInstances[i]}) - }); - - return groupedInstanceData + let jobsPerInstances = Array(setOfAllInstances.length).fill(0); + let failsPerInstances = Array(setOfAllInstances.length).fill(0); + let timesPerInstances = Array(setOfAllInstances.length).fill(0); + + allInstances.forEach(function (user, i) { + jobsPerInstances[setOfAllInstances.indexOf(user)] += 1 + failsPerInstances[setOfAllInstances.indexOf(user)] += ungroupedData[i].fails + timesPerInstances[setOfAllInstances.indexOf(user)] += ungroupedData[i].times + }); + + let groupedInstanceData = [] + + // when grouped by user: 'user', 'pool_label' and 'multiplier' can't be shown anymore + setOfAllInstances.forEach(function (elem, i) { + groupedInstanceData.push({ + unique_id: ungroupedData[i].unique_id, instances: elem, + times: timesPerInstances[i], fails: failsPerInstances[i], jobs: jobsPerInstances[i] + }) + }); + + return groupedInstanceData } function GroupByPoolLabel(ungroupedData) { - let allPoolLabel = ungroupedData.map(elem => elem.pool_labels); - allPoolLabel = allPoolLabel.filter(elem => elem !== null) + let allPoolLabel = ungroupedData.map(elem => elem.pool_labels); + allPoolLabel = allPoolLabel.filter(elem => elem !== null) - let setOfAllPoolLabel = [...new Set(allPoolLabel)]; + let setOfAllPoolLabel = [...new Set(allPoolLabel)]; - let jobsPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); - let failsPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); - let timesPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); - - allPoolLabel.forEach(function (user, i) { - jobsPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += 1 - failsPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += ungroupedData[i].fails - timesPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += ungroupedData[i].times - }); - - let groupedInstanceData = [] - - // when grouped by user: 'user', 'pool_label' and 'multiplier' can't be shown anymore - setOfAllPoolLabel.forEach(function (elem, i) { - groupedInstanceData.push({ unique_id: ungroupedData[i].unique_id, - pool_labels: elem, times: timesPerPoolLabel[i], - fails: failsPerPoolLabel[i], jobs: jobsPerPoolLabel[i]}) - }); - - return groupedInstanceData -} + let jobsPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); + let failsPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); + let timesPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); + + allPoolLabel.forEach(function (user, i) { + jobsPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += 1 + failsPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += ungroupedData[i].fails + timesPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += ungroupedData[i].times + }); + + let groupedInstanceData = [] + // when grouped by user: 'user', 'pool_label' and 'multiplier' can't be shown anymore + setOfAllPoolLabel.forEach(function (elem, i) { + groupedInstanceData.push({ + unique_id: ungroupedData[i].unique_id, + pool_labels: elem, times: timesPerPoolLabel[i], + fails: failsPerPoolLabel[i], jobs: jobsPerPoolLabel[i] + }) + }); + + return groupedInstanceData +} export default Quotas; From fb60c39429c7ac43d4c56ce23b7169c1de79ff66 Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 10:46:17 +0200 Subject: [PATCH 08/13] change column order --- src/components/Quotas.jsx | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 1489524..4546597 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -108,6 +108,17 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { sorter: "alphabetical", displayer: (user, _) => user }, + { + field: "token,is_hypercube", + column: "Job token", + displayer: (name, job_count) => <> + {job_count === true ? {name} + + HC + : + {name}} + + }, { field: "instances", column: "Instance", @@ -120,12 +131,6 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { sorter: "alphabetical", displayer: (pool_label) => pool_label == null ? '-' : pool_label }, - { - field: "times", - column: "Solve Time", - sorter: "numerical", - displayer: formatTime - }, { field: "fails", column: "Number Fails", @@ -138,22 +143,17 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { sorter: "alphabetical", displayer: String }, + { + field: "times", + column: "Solve Time", + sorter: "numerical", + displayer: formatTime + }, { field: "multipliers", column: "Multiplier", sorter: "alphabetical", displayer: String - }, - { - field: "token,is_hypercube", - column: "Job token", - displayer: (name, job_count) => <> - {job_count === true ? {name} - - HC - : - {name}} - } ]) @@ -175,13 +175,14 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { useEffect(() => { if (selectedAggregateType === '_') { - const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['token,is_hypercube'].includes(el.field)) + const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['jobs'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['jobs', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(ungroupedDataJobs) setTableDataPools(ungroupedDataPools) let sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) sumTmp += ungroupedDataPools.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) setTotalUsage(sumTmp) - setDisplayFieldsJobs(displayFieldUngrouped.current) + setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'username') { const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['instances', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) From 7d7822d0dc89ec132f607505c1e4ce62495689ba Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 11:32:57 +0200 Subject: [PATCH 09/13] add multh calculation --- src/components/CalculateQuota.js | 16 +++++++-- src/components/Quotas.jsx | 56 +++++++++++++++++++------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/components/CalculateQuota.js b/src/components/CalculateQuota.js index 3610c68..5794051 100644 --- a/src/components/CalculateQuota.js +++ b/src/components/CalculateQuota.js @@ -1,4 +1,4 @@ -function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput) { +function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput, quotaUnit) { const calcstartTime = calcstartTimeInput const calcEndTime = calcEndTimeInput @@ -358,19 +358,29 @@ function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput) { let ungroupedDataPools = [] calcTimesJobs['instances'].forEach(function (elem, i) { + let cost = calcTimesJobs.times[i] * calcTimesJobs.multipliers[i] + if (quotaUnit === 'multh') { + cost = cost / 3600 + } ungroupedDataJobs.push({ uniqueId: uniqueId[i], user: calcTimesJobs.users[i], instances: elem, pool_labels: calcTimesJobs.pool_labels[i], multipliers: calcTimesJobs.multipliers[i].toString(), times: calcTimesJobs.times[i], comments: calcTimesJobs.comments[i], fails: calcTimesJobs.fails[i], - jobs: '1', is_hypercube: calcTimesJobs.is_hypercube[i], token: calcTimesJobs.token[i]}) + jobs: '1', is_hypercube: calcTimesJobs.is_hypercube[i], token: calcTimesJobs.token[i], + cost: cost}) }); calcTimesPools['instances'].forEach(function (elem, i) { + let cost = calcTimesPools.times[i] * calcTimesPools.multipliers[i] + if (quotaUnit === 'multh') { + cost = cost / 3600 + } ungroupedDataPools.push({ unique_id: uniqueId[i], user: calcTimesPools.users[i], instances: elem, pool_labels: calcTimesPools.pool_labels[i], multipliers: calcTimesPools.multipliers[i].toString(), times: calcTimesPools.times[i], comments: calcTimesPools.comments[i], fails: calcTimesPools.fails[i], - jobs: '1', is_hypercube: calcTimesPools.is_hypercube[i], token: calcTimesPools.token[i]}) + jobs: '1', is_hypercube: calcTimesPools.is_hypercube[i], token: calcTimesPools.token[i], + cost: cost}) }); diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 4546597..3610108 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -30,15 +30,15 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { // const calcStartDate = new Date("2021-01-04T17:10:15.000000+00:00"); // const calcEndTime = new Date("2023-03-31T17:10:15.000000+00:00"); - const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) + const dataTmp = computeTimes(testData, calcStartDate, calcEndTime, quotaUnit) const [ungroupedDataJobs, setUngroupedDataJobs] = useState(dataTmp.data_jobs); const [ungroupedDataPools, setUngroupedDataPools] = useState(dataTmp.data_pools); useEffect(() => { - const dataTmp = computeTimes(testData, calcStartDate, calcEndTime) + const dataTmp = computeTimes(testData, calcStartDate, calcEndTime, quotaUnit) setUngroupedDataJobs(dataTmp.data_jobs) setUngroupedDataPools(dataTmp.data_pools) - }, [testData, calcStartDate, calcEndTime]) + }, [testData, calcStartDate, calcEndTime, quotaUnit]) function getChartData(label, ungroupedData) { @@ -154,6 +154,12 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { column: "Multiplier", sorter: "alphabetical", displayer: String + }, + { + field: "cost", + column: quotaUnit, + sorter: "numerical", + displayer: (cost) => Intl.NumberFormat('en-US', { style: 'decimal' }).format(cost)//(cost) => cost.toFixed(2) } ]) @@ -254,7 +260,7 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { options={availableAggregateTypes} /> -

Total: {new Intl.NumberFormat('en-US', { style: 'decimal' }).format(quotaUnit === 'mults' ? totalUsage: totalUsage /3600)} {quotaUnit}

+

Total: {new Intl.NumberFormat('en-US', { style: 'decimal' }).format(quotaUnit === 'mults' ? totalUsage: totalUsage / 3600)} {quotaUnit}

{truncateWarning !== '' &&
{truncateWarning}
} @@ -317,13 +323,15 @@ function GroupByUser(ungroupedData) { let setOfAllUsers = [...new Set(allUsers)]; let jobsPerUser = Array(setOfAllUsers.length).fill(0); - let FailsPerUser = Array(setOfAllUsers.length).fill(0); - let TimesPerUser = Array(setOfAllUsers.length).fill(0); + let failsPerUser = Array(setOfAllUsers.length).fill(0); + let timesPerUser = Array(setOfAllUsers.length).fill(0); + let costPerUser = Array(setOfAllUsers.length).fill(0); allUsers.forEach(function (user, i) { jobsPerUser[setOfAllUsers.indexOf(user)] += 1 - FailsPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].fails - TimesPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].times + failsPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].fails + timesPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].times + costPerUser[setOfAllUsers.indexOf(user)] += ungroupedData[i].cost }); let groupedUserData = [] @@ -332,8 +340,8 @@ function GroupByUser(ungroupedData) { setOfAllUsers.forEach(function (elem, i) { groupedUserData.push({ unique_id: ungroupedData[i].unique_id, user: elem, - times: TimesPerUser[i], fails: FailsPerUser[i], jobs: jobsPerUser[i] - }) + times: timesPerUser[i], fails: failsPerUser[i], jobs: jobsPerUser[i], + cost: costPerUser[i]}) }); return groupedUserData @@ -348,11 +356,13 @@ function GroupByInstance(ungroupedData) { let jobsPerInstances = Array(setOfAllInstances.length).fill(0); let failsPerInstances = Array(setOfAllInstances.length).fill(0); let timesPerInstances = Array(setOfAllInstances.length).fill(0); + let costPerInstances = Array(setOfAllInstances.length).fill(0); - allInstances.forEach(function (user, i) { - jobsPerInstances[setOfAllInstances.indexOf(user)] += 1 - failsPerInstances[setOfAllInstances.indexOf(user)] += ungroupedData[i].fails - timesPerInstances[setOfAllInstances.indexOf(user)] += ungroupedData[i].times + allInstances.forEach(function (instance, i) { + jobsPerInstances[setOfAllInstances.indexOf(instance)] += 1 + failsPerInstances[setOfAllInstances.indexOf(instance)] += ungroupedData[i].fails + timesPerInstances[setOfAllInstances.indexOf(instance)] += ungroupedData[i].times + costPerInstances[setOfAllInstances.indexOf(instance)] += ungroupedData[i].cost }); let groupedInstanceData = [] @@ -361,8 +371,8 @@ function GroupByInstance(ungroupedData) { setOfAllInstances.forEach(function (elem, i) { groupedInstanceData.push({ unique_id: ungroupedData[i].unique_id, instances: elem, - times: timesPerInstances[i], fails: failsPerInstances[i], jobs: jobsPerInstances[i] - }) + times: timesPerInstances[i], fails: failsPerInstances[i], jobs: jobsPerInstances[i], + cost: costPerInstances[i]}) }); return groupedInstanceData @@ -378,11 +388,13 @@ function GroupByPoolLabel(ungroupedData) { let jobsPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); let failsPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); let timesPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); + let costPerPoolLabel = Array(setOfAllPoolLabel.length).fill(0); - allPoolLabel.forEach(function (user, i) { - jobsPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += 1 - failsPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += ungroupedData[i].fails - timesPerPoolLabel[setOfAllPoolLabel.indexOf(user)] += ungroupedData[i].times + allPoolLabel.forEach(function (label, i) { + jobsPerPoolLabel[setOfAllPoolLabel.indexOf(label)] += 1 + failsPerPoolLabel[setOfAllPoolLabel.indexOf(label)] += ungroupedData[i].fails + timesPerPoolLabel[setOfAllPoolLabel.indexOf(label)] += ungroupedData[i].times + costPerPoolLabel[setOfAllPoolLabel.indexOf(label)] += ungroupedData[i].cost }); let groupedInstanceData = [] @@ -392,8 +404,8 @@ function GroupByPoolLabel(ungroupedData) { groupedInstanceData.push({ unique_id: ungroupedData[i].unique_id, pool_labels: elem, times: timesPerPoolLabel[i], - fails: failsPerPoolLabel[i], jobs: jobsPerPoolLabel[i] - }) + fails: failsPerPoolLabel[i], jobs: jobsPerPoolLabel[i], + cost: costPerPoolLabel[i]}) }); return groupedInstanceData From 9896bed1b4d3cc40f0a67a5fcedfc653ea17a6c1 Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 11:34:57 +0200 Subject: [PATCH 10/13] fix instance displayfield --- src/components/Quotas.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 3610108..41ef6fc 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -133,7 +133,7 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { }, { field: "fails", - column: "Number Fails", + column: "Number Crashes", sorter: "numerical", displayer: Number }, @@ -201,8 +201,8 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'instance') { - const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) - const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['user', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['user, unique_id', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['user, unique_id', 'pool_labels', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByInstance(ungroupedDataJobs)) setTableDataPools(GroupByInstance(ungroupedDataPools)) let sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) @@ -211,8 +211,8 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { setDisplayFieldsJobs(displayFieldsTmpJob) setDisplayFieldsPools(displayFieldsTmpPool) } else if (selectedAggregateType === 'pool_label') { - const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['instances', 'user', 'multipliers', 'token,is_hypercube'].includes(el.field)) - const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['instances', 'user', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['instances', 'user, unique_id', 'multipliers', 'token,is_hypercube'].includes(el.field)) + const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['instances', 'user, unique_id', 'multipliers', 'token,is_hypercube'].includes(el.field)) setTableDataJobs(GroupByPoolLabel(ungroupedDataJobs)) setTableDataPools(GroupByPoolLabel(ungroupedDataPools)) let sumTmp2 = ungroupedDataJobs.filter(el => el.pool_label != null).reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) From cb65da4f6699454c525191cab0d7cc94ab270c3b Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 12:18:41 +0200 Subject: [PATCH 11/13] change order in pool table --- src/components/Quotas.jsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 41ef6fc..06d8df3 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -163,6 +163,20 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { } ]) + function swaptDisplayFieldPool(field) { + let fieldTmp = [...field]; + + const idx1 = fieldTmp.findIndex(item => item.field === 'pool_labels'); + const idx2 = fieldTmp.findIndex(item => item.field === 'instances'); + + + const tmp = fieldTmp[idx1]; + fieldTmp[idx1] = fieldTmp[idx2]; + fieldTmp[idx2] = tmp; + + return fieldTmp + } + const [displayFieldsJobs, setDisplayFieldsJobs] = useState(displayFieldUngrouped.current); const [displayFieldsPools, setDisplayFieldsPools] = useState(displayFieldUngrouped.current); @@ -182,7 +196,8 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { useEffect(() => { if (selectedAggregateType === '_') { const displayFieldsTmpJob = displayFieldUngrouped.current.filter(el => !['jobs'].includes(el.field)) - const displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['jobs', 'token,is_hypercube'].includes(el.field)) + let displayFieldsTmpPool = displayFieldUngrouped.current.filter(el => !['jobs', 'token,is_hypercube'].includes(el.field)) + displayFieldsTmpPool = swaptDisplayFieldPool(displayFieldsTmpPool) setTableDataJobs(ungroupedDataJobs) setTableDataPools(ungroupedDataPools) let sumTmp = ungroupedDataJobs.reduce((accumulator, currentValue) => accumulator + currentValue.times * currentValue.multipliers, 0) From a585c7578af35444287687ab8242cc8103e4fea5 Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 12:23:15 +0200 Subject: [PATCH 12/13] change pie chart to use multh instead of time --- src/components/Quotas.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index 06d8df3..d5cb349 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -44,34 +44,34 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { function getChartData(label, ungroupedData) { let groupedData = [] let labels = [] - let times = [] + let cost = [] if (label === 'usernames') { groupedData = GroupByUser(ungroupedData); labels = groupedData.map(elem => elem.user); - times = groupedData.map(elem => elem.times); + cost = groupedData.map(elem => elem.cost); } else if (label === 'instances') { groupedData = GroupByInstance(ungroupedData); labels = groupedData.map(elem => elem.instances); - times = groupedData.map(elem => elem.times); + cost = groupedData.map(elem => elem.cost); } else if (label === 'pool_labels') { groupedData = GroupByPoolLabel(ungroupedData); labels = groupedData.map(elem => elem.pool_labels); - times = groupedData.map(elem => elem.times); + cost = groupedData.map(elem => elem.cost); } const cutOff = 20; if (labels.length > cutOff) { setTruncateWarning(current => `${current} Only the ${cutOff} most used ${label} displayed. `) - const labelTimePairs = labels.map((label, index) => ({ label, time: times[index] })); + const labelTimePairs = labels.map((label, index) => ({ label, cost: cost[index] })); // Sort the array of objects based on decreasing time - labelTimePairs.sort((a, b) => b.time - a.time); + labelTimePairs.sort((a, b) => b.cost - a.cost); // Extract the sorted labels and times separately labels = labelTimePairs.map(pair => pair.label); - times = labelTimePairs.map(pair => pair.time); + cost = labelTimePairs.map(pair => pair.cost); labels = labels.slice(0,cutOff); - times = times.slice(0,cutOff) + cost = cost.slice(0,cutOff) } return { @@ -79,7 +79,7 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { datasets: [ { label: '# of Votes', - data: times, + data: cost, backgroundColor: ["rgba(31,120,180,0.2)", "rgba(51,160,44,0.2)", "rgba(227,26,28,0.2)", "rgba(255,127,0,0.2)", "rgba(106,61,154,0.2)", "rgba(177,89,40,0.2)", From 22514c6dd2412fab84c2407d63f1e01e71f0bbd7 Mon Sep 17 00:00:00 2001 From: Janina Hasselbring Date: Mon, 31 Jul 2023 18:07:08 +0200 Subject: [PATCH 13/13] only show charts with at least two entries --- src/components/CalculateQuota.js | 17 +++++- src/components/Quotas.jsx | 101 ++++++++++++++++++++----------- 2 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/components/CalculateQuota.js b/src/components/CalculateQuota.js index 5794051..79ece65 100644 --- a/src/components/CalculateQuota.js +++ b/src/components/CalculateQuota.js @@ -363,7 +363,7 @@ function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput, quotaUn cost = cost / 3600 } ungroupedDataJobs.push({ uniqueId: uniqueId[i], user: calcTimesJobs.users[i], instances: elem, - pool_labels: calcTimesJobs.pool_labels[i], multipliers: calcTimesJobs.multipliers[i].toString(), + pool_labels: calcTimesJobs.pool_labels[i], multipliers: calcTimesJobs.multipliers[i], times: calcTimesJobs.times[i], comments: calcTimesJobs.comments[i], fails: calcTimesJobs.fails[i], jobs: '1', is_hypercube: calcTimesJobs.is_hypercube[i], token: calcTimesJobs.token[i], cost: cost}) @@ -377,16 +377,27 @@ function getComputationTimes(data, calcstartTimeInput, calcEndTimeInput, quotaUn cost = cost / 3600 } ungroupedDataPools.push({ unique_id: uniqueId[i], user: calcTimesPools.users[i], instances: elem, - pool_labels: calcTimesPools.pool_labels[i], multipliers: calcTimesPools.multipliers[i].toString(), + pool_labels: calcTimesPools.pool_labels[i], multipliers: calcTimesPools.multipliers[i], times: calcTimesPools.times[i], comments: calcTimesPools.comments[i], fails: calcTimesPools.fails[i], jobs: '1', is_hypercube: calcTimesPools.is_hypercube[i], token: calcTimesPools.token[i], cost: cost}) }); + let numberUsers = [...new Set(calcTimesPools.users.concat(calcTimesJobs.users))].length; + let numberInstances = [...new Set(calcTimesPools.instances.concat(calcTimesJobs.instances))].length; + + let allPools = [...new Set(calcTimesPools.pool_labels.concat(calcTimesJobs.pool_labels))] + allPools = allPools.filter(elem => elem !== null) + + let numberPools = allPools.length; + const result = { 'data_jobs': ungroupedDataJobs, - 'data_pools': ungroupedDataPools + 'data_pools': ungroupedDataPools, + 'num_users': numberUsers, + 'num_instances': numberInstances, + 'num_pools': numberPools } return ( diff --git a/src/components/Quotas.jsx b/src/components/Quotas.jsx index d5cb349..585fd1f 100644 --- a/src/components/Quotas.jsx +++ b/src/components/Quotas.jsx @@ -15,8 +15,7 @@ import { Link } from "react-router-dom"; ChartJS.register(ArcElement, Tooltip, Legend); - -const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { +const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit}) => { /* const data = test_data['test_hypercube_with_pool_and_job']; const calcStartDate = "2021-08-03T17:10:15.000000+00:00"; @@ -33,11 +32,25 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { const dataTmp = computeTimes(testData, calcStartDate, calcEndTime, quotaUnit) const [ungroupedDataJobs, setUngroupedDataJobs] = useState(dataTmp.data_jobs); const [ungroupedDataPools, setUngroupedDataPools] = useState(dataTmp.data_pools); + const [numUser, setNumUser] = useState(dataTmp.num_users); + const [numInstances, setNumInstances] = useState(dataTmp.num_instances); + const [numPools, setNumPools] = useState(dataTmp.num_pools); + const [numCharts, setNumCharts] = useState(dataTmp.num_pools); useEffect(() => { const dataTmp = computeTimes(testData, calcStartDate, calcEndTime, quotaUnit) setUngroupedDataJobs(dataTmp.data_jobs) setUngroupedDataPools(dataTmp.data_pools) + setNumUser(dataTmp.num_users) + setNumInstances(dataTmp.num_instances) + setNumPools(dataTmp.num_pools) + + let tmp = 0; + tmp = (dataTmp.num_users > 1) ? tmp +=1: tmp; + tmp = (dataTmp.num_instances > 1) ? tmp +=1: tmp; + tmp = (dataTmp.num_pools > 1) ? tmp +=1: tmp; + + setNumCharts(tmp) }, [testData, calcStartDate, calcEndTime, quotaUnit]) @@ -59,17 +72,18 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { cost = groupedData.map(elem => elem.cost); } + const labelTimePairs = labels.map((label, index) => ({ label, cost: cost[index] })); + + // Sort the array of objects based on decreasing time + labelTimePairs.sort((a, b) => b.cost - a.cost); + + // Extract the sorted labels and times separately + labels = labelTimePairs.map(pair => pair.label); + cost = labelTimePairs.map(pair => pair.cost); + const cutOff = 20; if (labels.length > cutOff) { setTruncateWarning(current => `${current} Only the ${cutOff} most used ${label} displayed. `) - const labelTimePairs = labels.map((label, index) => ({ label, cost: cost[index] })); - - // Sort the array of objects based on decreasing time - labelTimePairs.sort((a, b) => b.cost - a.cost); - - // Extract the sorted labels and times separately - labels = labelTimePairs.map(pair => pair.label); - cost = labelTimePairs.map(pair => pair.cost); labels = labels.slice(0,cutOff); cost = cost.slice(0,cutOff) } @@ -152,14 +166,14 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { { field: "multipliers", column: "Multiplier", - sorter: "alphabetical", - displayer: String + sorter: "numerical", + displayer: (mult) => Intl.NumberFormat('en-US', { style: 'decimal' }).format(mult) }, { field: "cost", column: quotaUnit, sorter: "numerical", - displayer: (cost) => Intl.NumberFormat('en-US', { style: 'decimal' }).format(cost)//(cost) => cost.toFixed(2) + displayer: (cost) => Intl.NumberFormat('en-US', { style: 'decimal' }).format(cost) } ]) @@ -242,24 +256,25 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { useEffect(() => { setTruncateWarning('') - let chartDataTmp = getChartData('usernames', ungroupedDataJobs) - if (chartDataTmp.labels.length === 0) { - chartDataTmp = { labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]} - } - setUserChartData(chartDataTmp) - chartDataTmp = getChartData('instances', ungroupedDataJobs) - if (chartDataTmp.labels.length === 0) { - chartDataTmp = { labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]} + let chartDataTmp = {}; + if (numUser > 1) { + chartDataTmp = getChartData('usernames', ungroupedDataJobs.concat(ungroupedDataPools)) + setUserChartData(chartDataTmp) } - setInstanceChartData(chartDataTmp) - chartDataTmp = getChartData('pool_labels', ungroupedDataJobs.concat(ungroupedDataPools)) - if (chartDataTmp.labels.length === 0) { - chartDataTmp = { labels: ['-'], datasets: [{label: '# of Votes', data: [1], backgroundColor: ["rgba(31,120,180,0.2)"]}]} + + if (numInstances > 1) { + chartDataTmp = getChartData('instances', ungroupedDataJobs.concat(ungroupedDataPools)) + setInstanceChartData(chartDataTmp) } - setPoolLabelChartData(chartDataTmp) - }, [ungroupedDataJobs, ungroupedDataPools]) + if (numPools > 1) { + chartDataTmp = getChartData('pool_labels', ungroupedDataJobs.concat(ungroupedDataPools)) + setPoolLabelChartData(chartDataTmp) + } + + }, [ungroupedDataJobs, ungroupedDataPools, numUser, numInstances, numPools]) + console.log(numCharts) return (
@@ -280,23 +295,35 @@ const Quotas = ({ testData, calcStartDate, calcEndTime, quotaUnit }) => { {truncateWarning}
}
-
-
-
+ {(numCharts > 0) ? ( +
+
+ {(numUser > 1) ? ( +

Users

-
+ ) :null} + {(numInstances > 1) ? ( +

Instances

-
-

Pools (With Idle)

+ ) :null} + {(numPools > 1) ? ( +
+

Pool *

-
+ ) :null}
-
+
+ ): null} +
0)?'9': '12')) + + ' col-lg-' + (((numCharts > 0)?'9': '12')) + ' col-md-12 col-12'}>

Jobs

{ idFieldName={'unique_id'} /> + {(numPools > 1) ? ( +
* includes Idle Times
+ ) :null} + ); }