2022-12-13 01:08:09 -07:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<title>Testing</title>
|
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
<script type="module" id="cuphead">
|
2022-12-13 01:08:09 -07:00
|
|
|
|
|
|
|
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
|
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
const jsonData = d3.json("http://localhost:8888");
|
2022-12-13 01:08:09 -07:00
|
|
|
jsonData.then(function (data){
|
2022-12-18 13:09:27 -07:00
|
|
|
let dataset = data.reports;
|
|
|
|
processData(dataset);
|
2022-12-13 01:08:09 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
function processData(data) {
|
2022-12-18 13:09:27 -07:00
|
|
|
let parsedCupheadData = [];
|
|
|
|
let indexWorking = 0;
|
|
|
|
let indexBroken = 1;
|
|
|
|
let prevDate = 14;
|
2022-12-13 01:08:09 -07:00
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
parsedCupheadData[indexWorking] = new Object();
|
|
|
|
parsedCupheadData[indexWorking].date = new Date("2019-11-01");
|
|
|
|
parsedCupheadData[indexWorking].title = "Working";
|
|
|
|
parsedCupheadData[indexWorking].number = 0;
|
|
|
|
|
|
|
|
parsedCupheadData[indexBroken] = new Object();
|
|
|
|
parsedCupheadData[indexBroken].date = new Date("2019-11-01");
|
|
|
|
parsedCupheadData[indexBroken].title = "Borked";
|
|
|
|
parsedCupheadData[indexBroken].number = 0;
|
2022-12-13 01:08:09 -07:00
|
|
|
|
|
|
|
for(let i = 0; i < data.length; i++) {
|
2022-12-18 13:09:27 -07:00
|
|
|
let currentReport = data[i];
|
|
|
|
let currentDate = new Date(data[i].timestamp * 1000);
|
|
|
|
let dateString = currentDate.getFullYear().toString() + "-" + currentDate.getMonth().toString() + "-" + currentDate.getDate().toString();
|
|
|
|
|
|
|
|
if(data[i].game_title != "Cuphead")
|
|
|
|
{continue;} else {
|
|
|
|
if(currentDate.getDate() != prevDate) {
|
|
|
|
prevDate = currentDate.getDate();
|
|
|
|
indexBroken = indexBroken + 2;
|
|
|
|
indexWorking = indexWorking + 2;
|
|
|
|
parsedCupheadData[indexWorking] = new Object();
|
|
|
|
parsedCupheadData[indexWorking].date = currentDate;
|
|
|
|
parsedCupheadData[indexWorking].title = "Working";
|
|
|
|
parsedCupheadData[indexWorking].number = 0;
|
2022-12-13 01:08:09 -07:00
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
parsedCupheadData[indexBroken] = new Object();
|
|
|
|
parsedCupheadData[indexBroken].date = currentDate;
|
|
|
|
parsedCupheadData[indexBroken].title = "Borked";
|
|
|
|
parsedCupheadData[indexBroken].number = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data[i].verdict == 0) {
|
|
|
|
parsedCupheadData[indexBroken].number++;
|
|
|
|
}
|
|
|
|
if(data[i].verdict == 1) {
|
|
|
|
parsedCupheadData[indexWorking].number++;
|
|
|
|
}
|
|
|
|
}
|
2022-12-13 01:08:09 -07:00
|
|
|
}
|
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
document.getElementById("visCuphead").appendChild(StackedAreaChart(parsedCupheadData, {
|
|
|
|
x: d => d.date,
|
|
|
|
y: d => d.number,
|
|
|
|
z: d => d.title,
|
|
|
|
yLabel: "Number of Reports--Cuphead",
|
|
|
|
width: 700,
|
|
|
|
height: 250
|
|
|
|
}
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Copyright 2021 Observable, Inc.
|
|
|
|
// Released under the ISC license.
|
|
|
|
// https://observablehq.com/@d3/normalized-stacked-area-chart
|
|
|
|
function StackedAreaChart(data, {
|
|
|
|
x = ([x]) => x, // given d in data, returns the (ordinal) x-value
|
|
|
|
y = ([, y]) => y, // given d in data, returns the (quantitative) y-value
|
|
|
|
z = () => 1, // given d in data, returns the (categorical) z-value
|
|
|
|
marginTop = 20, // top margin, in pixels
|
|
|
|
marginRight = 30, // right margin, in pixels
|
|
|
|
marginBottom = 30, // bottom margin, in pixels
|
|
|
|
marginLeft = 40, // left margin, in pixels
|
|
|
|
width = 640, // outer width, in pixels
|
|
|
|
height = 400, // outer height, in pixels
|
|
|
|
xType = d3.scaleUtc, // type of x-scale
|
|
|
|
xDomain, // [xmin, xmax]
|
|
|
|
xRange = [marginLeft, width - marginRight], // [left, right]
|
|
|
|
yType = d3.scaleLinear, // type of y-scale
|
|
|
|
yDomain, // [ymin, ymax]
|
|
|
|
yRange = [height - marginBottom, marginTop], // [bottom, top]
|
|
|
|
zDomain, // array of z-values
|
|
|
|
offset = d3.stackOffsetExpand, // stack offset method
|
|
|
|
order = d3.stackOrderNone, // stack order method
|
|
|
|
yLabel, // a label for the y-axis
|
|
|
|
xFormat, // a format specifier string for the x-axis
|
|
|
|
yFormat = "%", // a format specifier string for the y-axis
|
|
|
|
colors = d3.schemeDark2, // an array of colors for the (z) categories
|
|
|
|
} = {}) {
|
|
|
|
// Compute values.
|
|
|
|
const X = d3.map(data, x);
|
|
|
|
const Y = d3.map(data, y);
|
|
|
|
const Z = d3.map(data, z);
|
|
|
|
|
|
|
|
// Compute default x- and z-domains, and unique the z-domain.
|
|
|
|
if (xDomain === undefined) xDomain = d3.extent(X);
|
|
|
|
if (zDomain === undefined) zDomain = Z;
|
|
|
|
zDomain = new d3.InternSet(zDomain);
|
|
|
|
|
|
|
|
// Omit any data not present in the z-domain.
|
|
|
|
const I = d3.range(X.length).filter(i => zDomain.has(Z[i]));
|
2022-12-13 01:08:09 -07:00
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
// Compute a nested array of series where each series is [[y1, y2], [y1, y2],
|
|
|
|
// [y1, y2], …] representing the y-extent of each stacked rect. In addition,
|
|
|
|
// each tuple has an i (index) property so that we can refer back to the
|
|
|
|
// original data point (data[i]). This code assumes that there is only one
|
|
|
|
// data point for a given unique x- and z-value.
|
|
|
|
const series = d3.stack()
|
|
|
|
.keys(zDomain)
|
|
|
|
.value(([x, I], z) => Y[I.get(z)])
|
|
|
|
.order(order)
|
|
|
|
.offset(offset)
|
|
|
|
(d3.rollup(I, ([i]) => i, i => X[i], i => Z[i]))
|
|
|
|
.map(s => s.map(d => Object.assign(d, {i: d.data[1].get(s.key)})));
|
|
|
|
|
|
|
|
// Compute the default y-domain. Note: diverging stacks can be negative.
|
|
|
|
if (yDomain === undefined) yDomain = d3.extent(series.flat(2));
|
|
|
|
|
|
|
|
// Construct scales and axes.
|
|
|
|
const xScale = xType(xDomain, xRange);
|
|
|
|
const yScale = yType(yDomain, yRange);
|
|
|
|
const color = d3.scaleOrdinal(zDomain, colors);
|
|
|
|
const xAxis = d3.axisBottom(xScale).ticks(width / 80, xFormat).tickSizeOuter(0);
|
|
|
|
const yAxis = d3.axisLeft(yScale).ticks(height / 50, yFormat);
|
|
|
|
|
|
|
|
const area = d3.area()
|
|
|
|
.x(({i}) => xScale(X[i]))
|
|
|
|
.y0(([y1]) => yScale(y1))
|
|
|
|
.y1(([, y2]) => yScale(y2));
|
|
|
|
|
|
|
|
const svg = d3.create("svg")
|
|
|
|
.attr("width", width)
|
|
|
|
.attr("height", height)
|
|
|
|
.attr("viewBox", [0, 0, width, height])
|
|
|
|
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
|
|
|
|
|
|
|
|
svg.append("g")
|
|
|
|
.selectAll("path")
|
|
|
|
.data(series)
|
|
|
|
.join("path")
|
|
|
|
.attr("fill", ([{i}]) => color(Z[i]))
|
|
|
|
.attr("d", area)
|
|
|
|
.append("title")
|
|
|
|
.text(([{i}]) => Z[i]);
|
|
|
|
|
|
|
|
svg.append("g")
|
|
|
|
.attr("transform", `translate(0,${height - marginBottom})`)
|
|
|
|
.call(xAxis)
|
|
|
|
.call(g => g.select(".domain").remove());
|
|
|
|
|
|
|
|
svg.append("g")
|
|
|
|
.attr("transform", `translate(${marginLeft},0)`)
|
|
|
|
.call(yAxis)
|
|
|
|
.call(g => g.select(".domain").remove())
|
|
|
|
.call(g => g.selectAll(".tick line")
|
|
|
|
.filter(d => d === 0 || d === 1)
|
|
|
|
.clone()
|
|
|
|
.attr("x2", width - marginLeft - marginRight))
|
|
|
|
.call(g => g.append("text")
|
|
|
|
.attr("x", -marginLeft)
|
|
|
|
.attr("y", 10)
|
|
|
|
.attr("fill", "currentColor")
|
|
|
|
.attr("text-anchor", "start")
|
|
|
|
.text(yLabel));
|
|
|
|
|
|
|
|
return Object.assign(svg.node(), {scales: {color}});
|
|
|
|
}
|
|
|
|
}
|
2022-12-13 01:08:09 -07:00
|
|
|
</script>
|
|
|
|
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
|
2022-12-18 13:09:27 -07:00
|
|
|
<div>
|
|
|
|
<svg width="500" height="500" id="visCuphead" class="overflow"></svg>
|
2022-12-13 01:08:09 -07:00
|
|
|
</div>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|