chart = Plot.plot({
width: width,
height: height,
x: {domain: [0, 110], label: null},
y: {ticks: [], label: "Student"},
marginRight: 50,
legend: {marginBottom: 50},
color: {domain: allAssignmnets, range: colorRange, legend: true, width: 500, columns:1},
marks: [
Plot.text(gradeBoundaries, {x: "x", text: "grade", textAnchor: "start", dx: 5, dy: -280}),
Plot.text(gradeBoundaries, {x: "x", text: "grade", textAnchor: "start", dx: 5, dy: 270}),
Plot.ruleX([0, 30, 40, 50, 60, 63, 67, 70, 73, 77, 80, 83, 87, 90, 93, 100]),
Plot.barX(tidydata.filter(f => f.points && f.assignment !== "total"), {
x: "points",
y: "student",
fill: "assignment",
stroke: "white",
sort: {y: "-x"},
tip: true
}),
showForecast ?
Plot.link(tidydata.filter(f => f.assignment === "total"), {
x1: d => d["points"] * forecastFactor + groupProjectMin,
x2: d => d["points"] * forecastFactor + groupProjectMax,
y1: d => d["student"],
y2: d => d["student"],
markerEnd: "dot",
markerStart: "dot",
strokeWidth: 2,
stroke: "black"
}) : null,
showForecast ?
Plot.text(tidydata.filter(f => f.assignment === "total"), {
x: d => d["points"] * forecastFactor + groupProjectMin,
y: "student",
text: d => d["points"] === d3.max(tidydata, a => a.points) ? "FORECAST low & high": "",
textAnchor: "start",
dx: 5,
dy: -10,
stroke: "white",
strokeWidth: 4,
paintOrder: "stroke",
fill: "black",
fontSize: "1em"
}) : null,
Plot.text(tidydata.filter(f => f.assignment === "total"), {
x: "points",
y: "student",
text: (d) => Math.floor(d.points),
textAnchor: "start",
fill: "black",
stroke: "white",
strokeWidth: 3,
paintOrder: "stroke",
dx: 2
}),
Plot.image(tidydata.slice(0,1), {
x: width,
y: "student",
width: 200,
src: "https://i.redd.it/1o8zqjm9bqc81.png"
})
]
})