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.
graphUsageasync 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.