The code uses the D3.js library to create a graph that displays system usage data over time, with the option to use a fallback data source if the primary data is missing. The graph is customized with a color palette and axis labels, and it displays a line for each group in the data.
npm run import -- "system usage graph"
import { D3Node } from 'd3-node'
const systemUsage = importer.import("system usage report")
async function graphUsage(data) {
if(!data) {
let usage = await systemUsage()
let now = Date.now()
data = usage.cpus.map((t, i) => ({
n: t,
year: now - i * 1000,
name: 'cpu'
})).concat(usage.memory.map((t, i) => ({
n: t,
year: now - i * 1000,
name: 'mem'
}))).sort((a, b) => a.name - b.name)
console.log(data)
}
// set the dimensions and margins of the graph
const margin = {top: 10, right: 30, bottom: 30, left: 60},
width = 460 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
const d3n = new D3Node(); // initializes D3 with container element
const d3 = d3n.d3;
// append the svg object to the body of the page
const svg = d3n.createSVG(
width + margin.left + margin.right,
height + margin.top + margin.bottom)
/*const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
*/
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// group the data: I want to draw one line per group
const sumstat = d3.group(data, d => d.name); // nest function allows to group the calculation per level of a factor
// Add X axis --> it is a date format
const x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.year; }))
.range([ 0, width ]);
svg.append("g")
.attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
const y = d3.scaleLinear()
.domain([0, 100 /* d3.max(data, function(d) { return +d.n; }) */])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
// color palette
const color = d3.scaleOrdinal()
.range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'])
// Draw the line
svg.selectAll(".line")
.data(sumstat)
.join("path")
.attr("fill", "none")
.attr("stroke", function(d){ return color(d[0]) })
.attr("stroke-width", 1.5)
.attr("d", function(d){
return d3.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y(+d.n); })
(d[1])
})
return d3n.svgString();
}
export default graphUsage
import { D3Node } from 'd3-node'
import systemUsage from './systemUsage'; // assuming systemUsage is a separate module
/**
* Graphs system usage data.
*
* @param {Object[]} data - System usage data.
* @returns {Promise<string>} SVG string representation of the graph.
*/
async function graphUsage(data) {
if (!data) {
// TODO: Consider caching system usage data to prevent repeated calls.
const usage = await systemUsage();
const now = Date.now();
data = usage.cpus
.map((t, i) => ({ n: t, year: now - i * 1000, name: 'cpu' }))
.concat(usage.memory.map((t, i) => ({ n: t, year: now - i * 1000, name:'mem' }))
).sort((a, b) => a.name.localeCompare(b.name)); // use localeCompare for string comparison
console.log(data);
}
const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 460 - margin.left - margin.right;
const height = 200 - margin.top - margin.bottom;
const d3n = new D3Node();
const d3 = d3n.d3;
const svg = d3n.createSVG(
width + margin.left + margin.right,
height + margin.top + margin.bottom
).append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Group data by name
const sumstat = d3.group(data, (d) => d.name);
// Set up scales
const x = d3.scaleTime() // Use scaleTime for date scales
.domain(d3.extent(data, (d) => d.year))
.range([0, width]);
const y = d3.scaleLinear()
.domain([0, Math.max(...data.map((d) => d.n))]) // Use Math.max for domain calculation
.range([height, 0]);
// Add axes
svg.append("g")
.attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(x).ticks(5));
svg.append("g")
.call(d3.axisLeft(y));
// Color palette
const color = d3.scaleOrdinal()
.range([
'#e41a1c',
'#377eb8',
'#4daf4a',
'#984ea3',
'#ff7f00',
'#ffff33',
'#a65628',
'#f781bf',
'#999999'
]);
// Draw the line
const lines = svg.selectAll(".line")
.data(sumstat)
.join("path")
.attr("fill", "none")
.attr("stroke", (d) => color(d[0]))
.attr("stroke-width", 1.5)
.attr("d", (d) => d3.line()
.x((d) => x(d.year))
.y((d) => y(+d.n))
(d[1])
);
return d3n.svgString();
}
export default graphUsage;
import { D3Node } from 'd3-node'
const systemUsage = importer.import('system usage report')
The code imports the D3Node
module from the d3-node
library, which is used for creating a D3.js context. It also imports a function systemUsage
from a module named system usage report
.
graphUsage
async function graphUsage(data) {
//...
}
The graphUsage
function is defined as an asynchronous function that takes a single argument data
.
if (!data) {
let usage = await systemUsage()
//...
}
If the data
argument is falsy, the function awaits the result of calling systemUsage()
and stores the result in usage
. It then generates data by mapping over the cpus
and memory
properties of the usage
object.
const margin = { top: 10, right: 30, bottom: 30, left: 60 },
width = 460 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
const d3n = new D3Node();
const d3 = d3n.d3;
// append the svg object to the body of the page
const svg = d3n.createSVG(
width + margin.left + margin.right,
height + margin.top + margin.bottom)
//...
The code sets up the margins, width, and height of the graph. It creates a new instance of D3Node
and gets the D3.js context. It then creates an SVG element with the specified width and height.
const sumstat = d3.group(data, d => d.name);
// Add X axis --> it is a date format
const x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.year; }))
.range([ 0, width ]);
svg.append("g")
.attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
const y = d3.scaleLinear()
.domain([0, 100 /* d3.max(data, function(d) { return +d.n; }) */])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
// color palette
const color = d3.scaleOrdinal()
.range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'])
// Draw the line
svg.selectAll(".line")
.data(sumstat)
.join(
enter => enter.append("path")
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", (d, i) => color(i))
.attr("stroke-width", 1.5)
.attr("d", d => d[0].x0 + " " + d[0].y0 + " " + d[1].x1 + " " + d[1].y1),
update => update,
exit => exit.remove()
)
The code groups the data by the name
property and sets up the X and Y axes. It defines a color palette and draws a line for each group in the data.
The last part of the code is incomplete and contains a typo. The variable sumsta
should be sumstat
.