Skip to content
Snippets Groups Projects
Commit 3d079ef2 authored by 0x539's avatar 0x539
Browse files

grouped bar chart update

parent fa142122
Branches
No related tags found
No related merge requests found
Source diff could not be displayed: it is too large. Options to address this: view the blob.
import "./src/css/styles.scss"
import { getResult, initGrid } from "./src/js/utils";
import { BaseChartWidget, LineChartWidget, BarChartWidget, AreaChartWidget, PieChartWidget, StackedBarChartWidget } from "./src/js/charts";
import { BaseChartWidget } from "./src/js/charts/base";
import { LineChartWidget } from"./src/js/charts/linechart";
import { BarChartWidget } from "./src/js/charts/barchart";
import { PieChartWidget } from "./src/js/charts/piechart";
import { AreaChartWidget } from "./src/js/charts/areachart";
import { StackedBarChartWidget } from "./src/js/charts/stacked_barchart";
import { GroupedBarChart } from "./src/js/charts/grouped_barchart";
export {
getResult,
......@@ -10,5 +16,6 @@ export {
AreaChartWidget,
PieChartWidget,
StackedBarChartWidget,
BaseChartWidget
BaseChartWidget,
GroupedBarChart
};
import "../css/styles.scss"
import "./main"
\ No newline at end of file
import * as d3 from "d3";
import {BaseChartWidget} from "./base";
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/grouped-bar-chart
export class GroupedBarChart extends BaseChartWidget{
constructor(titleText, description, data, {
transform, // data transformation function
x = (d, i) => i, // given d in data, returns the (ordinal) x-value
y = d => d, // given d in data, returns the (quantitative) y-value
z = () => 1, // given d in data, returns the (categorical) z-value
title, // given d in data, returns the title text
marginTop = 0, // top margin, in pixels
marginRight = 0, // right margin, in pixels
marginBottom = 0, // bottom margin, in pixels
marginLeft = 40, // left margin, in pixels
width = 640, // outer width, in pixels
height = 400, // outer height, in pixels
xDomain, // array of x-values
xRange = [marginLeft, width - marginRight], // [xmin, xmax]
xPadding = 0.1, // amount of x-range to reserve to separate groups
yType = d3.scaleLinear, // type of y-scale
yDomain, // [ymin, ymax]
yRange = [height - marginBottom, marginTop], // [ymin, ymax]
zDomain, // array of z-values
zPadding = 0.05, // amount of x-range to reserve to separate bars
yFormat, // a format specifier string for the y-axis
yLabel, // a label for the y-axis
colors = d3.schemeTableau10, // array of colors
drawLegend = true
}) {
if (transform)
data = (data ?? []).map(transform);
const options = {
transform,
x,
y,
z,
title,
marginTop,
marginRight,
marginBottom,
marginLeft,
width,
height,
xDomain,
xRange,
xPadding,
yType,
yDomain,
yRange,
zDomain,
zPadding,
yFormat, // a format specifier string for the y-axis
yLabel, // a label for the y-axis
colors,
drawLegend
}
super(title, description, data, options);
this.options = options
this.data = data
this.wrapper = document.createElement("div");
this.svg = d3.select(this.wrapper).append("svg");
}
plot(divWidth, divHeight) {
let [w, h] = this.clearAndScaleSvg(divWidth, divHeight);
// this.drawTitle();
let {
x,
y,
z,
title,
marginTop,
marginRight,
marginBottom,
marginLeft,
width,
height,
xDomain,
xRange,
xPadding,
yType,
yDomain,
yRange,
zDomain,
zPadding,
yFormat, // a format specifier string for the y-axis
yLabel, // a label for the y-axis
colors,
drawLegend
} = this.options
const labelOffset = 15
width = w
height = h + 150
marginTop = labelOffset * (zDomain??[]).length
marginRight = this.marginRight
marginBottom = this.marginBottom
yRange = [height - marginBottom, marginTop]
xRange = [marginLeft, width - marginRight]
const data = this.data
const X = d3.map(data, x);
const Y = d3.map(data, y);
const Z = d3.map(data, z);
// Compute default domains, and unique the x- and z-domains.
if (xDomain === undefined) xDomain = X;
if (yDomain === undefined) yDomain = [0, d3.max(Y)];
if (zDomain === undefined) zDomain = Z;
xDomain = new d3.InternSet(xDomain);
zDomain = new d3.InternSet(zDomain);
// Omit any data not present in both the x- and z-domain.
const I = d3.range(X.length).filter(i => xDomain.has(X[i]) && zDomain.has(Z[i]));
// Construct scales, axes, and formats.
const xScale = d3.scaleBand(xDomain, xRange).paddingInner(xPadding);
const xzScale = d3.scaleBand(zDomain, [0, xScale.bandwidth()]).padding(zPadding);
const yScale = yType(yDomain, yRange);
const zScale = d3.scaleOrdinal(zDomain, colors);
const xAxis = d3.axisBottom(xScale).tickSizeOuter(0);
const yAxis = d3.axisLeft(yScale).ticks(height / 60, yFormat);
// Compute titles.
if (title === undefined) {
const formatValue = yScale.tickFormat(100, yFormat);
title = i => `${X[i]}\n${Z[i]}\n${formatValue(Y[i])}`;
} else {
const O = d3.map(data, d => d);
const T = title;
title = i => T(O[i], i, data);
}
this.g.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(yAxis)
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - marginLeft - marginRight)
.attr("stroke-opacity", 0.1))
.call(g => g.append("text")
.attr("x", -marginLeft)
.attr("y", 100)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text(yLabel));
this.g.append("g")
.selectAll("rect")
.data(I)
.join("rect")
.attr("x", i => xScale(X[i]) + xzScale(Z[i]))
.attr("y", i => yScale(Y[i]))
.attr("width", xzScale.bandwidth())
.attr("height", i => yScale(0) - yScale(Y[i]))
.attr("fill", i => zScale(Z[i]))
if(drawLegend)
{
// custom legend
let runner = 2;
(zDomain??[]).forEach( (ele) => {
runner+=1
const color = zScale(ele)
const yOffset = (labelOffset * runner) / 2
const xOffset = runner%2? 0 : 200
this.g.append("circle").attr("cx",xOffset).attr("cy",yOffset).attr("r", 5).style("fill",color)
this.g.append("text").attr("x", xOffset + 10).attr("y", yOffset +4).text(ele).style("font-size", "15px").attr("alignment-baseline","middle")
})
}
if (title) this.g.append("title")
.text(title);
this.g.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(xAxis);
return this.wrapper.innerHTML;
}
}
\ No newline at end of file
import { BaseChartWidget } from "./base";
import { LineChartWidget } from "./linechart";
import { BarChartWidget } from "./barchart";
import { PieChartWidget } from "./piechart";
import { AreaChartWidget } from "./areachart";
import { StackedBarChartWidget } from "./stacked_barchart";
export {
BaseChartWidget,
LineChartWidget,
BarChartWidget,
PieChartWidget,
AreaChartWidget,
StackedBarChartWidget,
};
import { getResult, initGrid } from "./utils";
import {
LineChartWidget,
BarChartWidget,
AreaChartWidget,
PieChartWidget,
StackedBarChartWidget,
} from "./charts";
import { CourseRatingChart } from "./charts/custom/courseRatingChart";
/**
* JWT Token - hardcoded for demotrastion
* This jwt token is generated in the backend.
*/
const TOKEN =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJjb250ZXh0X2lkIjoidXNlcjFAcG9sYXJpcy5jb20iLCJleHAiOjE2NzQwNDc1MTUsImVuZ2luZXMiOlsyLDQsNSw2LDUsMTNdLCJlbmdpbmVzX3dpdGhfY29udGV4dCI6WzExLDEyXX0.ASnyAZ9jalb2vzx4zoT7tf9_BLhgsHfLt0GRLYag-9AqojLbL7y6NKr_3eKNVmE5yylYttN705zaTVW8pzGAfmYpM0OFlWwr1Je_AXd3sNvAxERjV2cBNg5uDggi4gUCT6xqQ7K3nwy1l_q1SsWBPHmhDmcJbVmfH3Y0DNeUaf0WyXjKDLIalKLQV9DwuRUcDUqq6FT0P9EG1_DyBTihJKEInD4gNQzNHwTibJrLdK8UMz_PeDRZT1jrv40bsKoyc4vIJvZdiE9q3ti93fZXLkc9I6OOqsBqEl7l2H2WEuEEUXoLEK0j2tb7_X367y8d9i-7jLuvc406uFVdAqPQ_XoRK689eL7wPDwl2KU7mvrZv1mIxnxe-OIVAPLq8LTzMFdCnf1rdDORLRjjAHOCrszE_2oKE9ciE3pW_0OxeOkR8dAX6uKXIURgZd2fYIO4Cn97LQBTEGR04eKe2YAIovGM9_SqC2x10H7VwcEkHLHpUTNLeR9rrrNu36hPgIvH";
const URL = "http://vs-code-cloud.digitallearning.gmbh:8003/api/v1/provider/result"
/**
* Setup initial widget position and sizes
*/
const items = [
{
x: 4,
y: 0,
w: 4,
h: 8,
widgetId: "second-widget",
},
{
x: 4,
y: 0,
w: 4,
h: 4,
widgetId: "fourth-widget",
},
{
x: 8,
y: 4,
w: 4,
h: 8,
widgetId: "sixth-widget",
},
{
x: 4,
y: 8,
w: 4,
h: 8,
widgetId: "seventh-widget",
},
{
x: 8,
y: 8,
w: 4,
h: 8,
widgetId: "eigth-widget",
},
{
x: 8,
y: 12,
w: 4,
h: 8,
widgetId: "ninth-widget",
},
{
x: 0,
y: 12,
w: 4,
h: 8,
widgetId: "tenth-widget",
},
{
x: 0,
y: 12,
w: 4,
h: 8,
widgetId: "eleventh-widget",
},
];
/**
* Get analytics engine results and render widgets
*/
getResult(TOKEN, URL).then((data) => {
// Create widgets with data from analytics engine results
const widgets = {
"second-widget": new BarChartWidget(
"Statements H5P",
data["count_h5p_statements"]?.description,
data["count_h5p_statements"]?.latest_result,
{
xAxisLabel: "Datum",
yAxisLabel: "#Statements",
transform: (d) => ({
column1: new Date(d.column1 * 1000),
column2: d.column2,
}),
}
),
"fourth-widget": new PieChartWidget(
"Persönliche Statement Verteilung",
data["h5p_statements_distribution"]?.description,
data["h5p_statements_distribution"]?.latest_result,
{
showLegend: true,
xAxisLabel: "Jahr",
yAxisLabel: "#Akitivitäten",
}
),
"sixth-widget": new LineChartWidget(
"Semesterabschluss",
data["collect_h5p_count_statements"]?.description,
data["collect_h5p_count_statements"]?.latest_result,
{
xAxisLabel: "Monat",
yAxisLabel: "Index",
transform: (d) => ({
column1: new Date(d.column1 * 1000),
column2: d.column2,
}),
}
),
"seventh-widget": new CourseRatingChart(
"Bewertungen für Kurse",
data["random_course_rating"]?.description,
data["random_course_rating"]?.latest_result,
{
xAxisLabel: "Note",
yAxisLabel: "Kurs",
}
),
"eigth-widget": new AreaChartWidget(
"Statements H5P",
data["collect_h5p_count_statements"]?.description,
data["collect_h5p_count_statements"]?.latest_result,
{
xAxisLabel: "Monat",
yAxisLabel: "Index",
transform: (d) => ({
column1: new Date(d.column1 * 1000),
column2: d.column2,
}),
}
),
"ninth-widget": new BarChartWidget(
"Statements Moodle",
data["count_moodle_statements"]?.description,
data["count_moodle_statements"]?.latest_result,
{
xAxisLabel: "Datum",
yAxisLabel: "#Statements",
transform: (d) => ({
column1: new Date(d.column1 * 1000),
column2: d.column2,
}),
}
),
"tenth-widget": new StackedBarChartWidget(
"Statements",
data["collect_counts_all_providers"]?.description,
data["collect_counts_all_providers"]?.latest_result,
["overall"],
{
showLegend: true,
xAxisLabel: "Provider",
yAxisLabel: "#Statements",
}
),
"eleventh-widget": new BarChartWidget(
"Personal H5P xAPI Statements",
data["h5p_count_user_statements"]?.description,
data["h5p_count_user_statements"]?.latest_result,
{
xAxisLabel: "Datum",
yAxisLabel: "#Statements",
transform: (d) => ({
column1: new Date(d.column1 * 1000),
column2: d.column2,
}),
}
),
};
// Initialize grid with widgets at configured positions
const grid = initGrid(widgets, items);
// Handle toggle button click
const toggleBtn = document.getElementById("toggle-sidebar-btn");
toggleBtn.onclick = grid.toggleSidebar;
// Handle save button click
const saveBtn = document.getElementById("save-btn");
saveBtn.onclick = () => {
// Get current grid configuration -> we might want to store this configuration
const oldGrid = grid.save();
// Reload grid with saved grid configuration (just for demonstration)
grid.grid.removeAll();
setTimeout(() => {
grid.load(oldGrid);
}, 2000);
};
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment