Skip to content
Snippets Groups Projects
Commit 76eef86e authored by Bjarke Madsen's avatar Bjarke Madsen
Browse files

Refactor FundingSource page

parent 46192842
Branches
Tags
No related merge requests found
......@@ -27,26 +27,22 @@ export interface Budget {
}
export interface FundingSource {
CLIENT_INSTITUTIONS: string,
COMMERCIAL: string,
EUROPEAN_FUNDING: string,
GOV_PUBLIC_BODIES: string,
CLIENT_INSTITUTIONS: number,
COMMERCIAL: number,
EUROPEAN_FUNDING: number,
GOV_PUBLIC_BODIES: number,
OTHER: number,
NREN: string,
OTHER: string,
YEAR: number,
id: number
}
export interface FS {
data: [FundingSource]
}
export interface FundingGraphMatrix {
export interface FundingSourceDataset {
labels: string[],
datasets: {
label: string,
data: string[],
data: number[],
backgroundColor: string
borderRadius: number,
borderSkipped: boolean,
......
import { cartesianProduct } from 'cartesian-product-multiple-arrays';
import {
FundingSource,
FundingSourceDataset
} from "../Schema";
const DEFAULT_FUNDING_SOURCE_DATA = [
{
"CLIENT_INSTITUTIONS": "0.0",
"COMMERCIAL": "0.0",
"EUROPEAN_FUNDING": "0.0",
"GOV_PUBLIC_BODIES": "0.0",
"NREN": "",
"OTHER": "0.0",
"YEAR": 0,
"id": 0
}]
function getColorMap() {
const rgbToHex = (r: number, g: number, b: number) => '#' + [r, g, b].map(x => {
const hex = x.toString(16)
return hex.length === 1 ? '0' + hex : hex
}).join('')
let colorMap = new Map<string, string>();
colorMap.set("CLIENT INSTITUTIONS", rgbToHex(157, 40, 114))
colorMap.set("COMMERCIAL", rgbToHex(241, 224, 79))
colorMap.set("EUROPEAN FUNDING", rgbToHex(219, 42, 76))
colorMap.set("GOV/PUBLIC_BODIES", rgbToHex(237, 141, 24))
colorMap.set("OTHER", rgbToHex(137, 166, 121))
return colorMap
}
function CreateDataLookup(data: FundingSource[]) {
let dataLookup = new Map<string, Map<string, number>>();
data.forEach((item: FundingSource) => {
const lookupKey = `${item.NREN}/${item.YEAR}`
let fundingSourceMap = dataLookup.get(lookupKey)
if (!fundingSourceMap) {
fundingSourceMap = new Map<string, number>();
}
fundingSourceMap.set("CLIENT INSTITUTIONS", item.CLIENT_INSTITUTIONS)
fundingSourceMap.set("COMMERCIAL", item.COMMERCIAL)
fundingSourceMap.set("EUROPEAN FUNDING", item.EUROPEAN_FUNDING)
fundingSourceMap.set("GOV/PUBLIC_BODIES", item.GOV_PUBLIC_BODIES)
fundingSourceMap.set("OTHER", item.OTHER)
dataLookup.set(lookupKey, fundingSourceMap)
})
return dataLookup
}
export const createFundingSourceDataset = (fundingSourcesData: FundingSource[]) => {
const data = fundingSourcesData ?? DEFAULT_FUNDING_SOURCE_DATA;
const dataLookup = CreateDataLookup(data)
const labelsYear = [...new Set(data.map((item: FundingSource) => item.YEAR))];
const labelsNREN = [...new Set(data.map((item: FundingSource) => item.NREN))];
const fundingSources = [
"CLIENT INSTITUTIONS",
"COMMERCIAL",
"EUROPEAN FUNDING",
"GOV/PUBLIC_BODIES",
"OTHER"
]
const fundingSourcesPerYear = cartesianProduct(fundingSources, labelsYear)
const colorMap = getColorMap();
const fundingSourceDataset = fundingSourcesPerYear.map(function ([fundingSource, year]) {
const color = colorMap.get(fundingSource)!;
return {
backgroundColor: color,
label: fundingSource + "(" + year + ")",
data: labelsNREN.map(nren => {
// ensure that the data is in the same order as the labels
const lookupKey = `${nren}/${year}`
const dataForYear = dataLookup.get(lookupKey)
if (!dataForYear) {
return 0
}
return dataForYear.get(fundingSource) ?? 0
}),
stack: year,
borderRadius: 10,
borderSkipped: false,
barPercentage: 0.5,
borderWidth: 0.5,
categoryPercentage: 0.8
}
})
const dataResponse: FundingSourceDataset = {
// datasets: datasetFunding,
datasets: fundingSourceDataset,
labels: labelsNREN.map(l => l.toString())
}
return dataResponse;
}
\ No newline at end of file
import React, { ReactElement, useEffect, useState } from 'react';
import { ChartOptions, scales, Tick } from 'chart.js';
import React, { useEffect, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { cartesianProduct } from 'cartesian-product-multiple-arrays';
import { createFundingSourceDataset } from "../helpers/dataconversion";
import {
Chart as ChartJS,
......@@ -13,14 +12,9 @@ import {
Legend,
} from 'chart.js';
import {
Budget,
BudgetMatrix,
DataEntrySection,
FundingSource,
FS, FundingGraphMatrix
FundingSourceDataset
} from "../Schema";
// import _default from "chart.js/dist/plugins/plugin.tooltip";
// import numbers = _default.defaults.animations.numbers;
ChartJS.register(
......@@ -32,15 +26,34 @@ ChartJS.register(
Legend
);
export const option = {
const EMPTY_DATASET = {
datasets: [
{
backgroundColor: '',
data: [],
label: '',
borderRadius: 0,
borderSkipped: false,
barPercentage: 0,
borderWidth: 0,
stack: '0',
categoryPercentage: 0.5
}],
labels: []
}
export const chartOptions = {
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
labels: {
boxWidth: 20,
boxHeight: 30,
pointStyle: "rectRounded",
borderRadius: 6,
useBorderRadius: true,
},
},
},
......@@ -60,209 +73,48 @@ export const option = {
stacked: true,
},
},
indexAxis: 'y',
indexAxis: "y" as const
};
function FundingSourcePage(): ReactElement {
function api<T>(url: string, options: RequestInit | undefined = undefined): Promise<T> {
return fetch(url, options)
.then((response) => {
if (!response.ok) {
return response.text().then((message) => {
console.error(`Failed to load datax: ${message}`, response.status);
throw new Error("The data could not be loaded, check the logs for details.");
});
}
return response.json() as Promise<T>;
})
async function getData(): Promise<FundingSource[]> {
try {
const response = await fetch('/api/funding/');
return response.json();
} catch (error) {
console.error(`Failed to load data: ${error}`);
throw error;
}
}
const [fundingMatrixResponse, setFundingMatrixResponse] = useState<FundingGraphMatrix>();
const [fundingSourceResponse, setFundingSource] = useState<FundingSource[]>();
const [dataEntrySection, setDataEntrySection] = useState<DataEntrySection>();
const [selectedDataEntry, setSelectedDataEntry] = useState<number>(0);
function FundingSourcePage() {
const [fundingSourceDataset, setDataset] = useState<FundingSourceDataset>();
const [fundingSourceData, setFundingSourceData] = useState<FundingSource[]>();
useEffect(() => {
const loadData = async () => {
if (fundingSourceResponse == undefined) {
api<FS>('/api/funding/', {})
.then((fundingSources: FS) => {
console.log('fundingSource:', fundingSources)
const entry = dataEntrySection?.items.find(i => i.id == selectedDataEntry)
console.log(selectedDataEntry, dataEntrySection, entry)
if (entry)
console.log("hello")
// options.plugins.title.text = entry.title;
setFundingSource(fundingSources.data)
convertToFundingSourcePerYearDataResponse(fundingSources.data)
})
.catch(error => {
console.log(`Error fetching from API: ${error}`);
})
} else {
convertToFundingSourcePerYearDataResponse(fundingSourceResponse)
}
const _fundingData = await getData()
setFundingSourceData(_fundingData)
}
loadData()
}, [])
const empty_funding_source_response = [
{
"CLIENT_INSTITUTIONS": "0.0",
"COMMERCIAL": "0.0",
"EUROPEAN_FUNDING": "0.0",
"GOV_PUBLIC_BODIES": "0.0",
"NREN": "",
"OTHER": "0.0",
"YEAR": 0,
"id": 0
}]
const convertToFundingSourcePerYearDataResponse = (fundingSourcesResponse: FundingSource[]) => {
const fsResponse = fundingSourcesResponse != undefined ? fundingSourcesResponse : empty_funding_source_response;
const labelsYear = [...new Set(fsResponse.map((item: FundingSource) => item.YEAR))];
const labelsNREN = [...new Set(fsResponse.map((item: FundingSource) => item.NREN))];
const fundingComposition = [
"CLIENT INSTITUTIONS",
"COMMERCIAL",
"EUROPEAN FUNDING",
"GOV/PUBLIC_BODIES",
"OTHER"
]
const dataSetKey = cartesianProduct(fundingComposition, labelsYear)
console.log("Nrens : ", labelsNREN)
console.log("Years : ", labelsYear)
console.log(dataSetKey);
function getRandomColor() {
const red = Math.floor(Math.random() * 256).toString(16).padStart(2, '0'); // generates a value between 00 and ff
const green = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
const blue = Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
return `#${red}${green}${blue}`;
}
const rgbToHex = (r: number, g: number, b: number) => '#' + [r, g, b].map(x => {
const hex = x.toString(16)
return hex.length === 1 ? '0' + hex : hex
}).join('')
let colorMap = new Map<string, string>();
colorMap.set("CLIENT INSTITUTIONS", rgbToHex(157, 40, 114))
colorMap.set("COMMERCIAL", rgbToHex(241, 224, 79))
colorMap.set("EUROPEAN FUNDING", rgbToHex(219, 42, 76))
colorMap.set("GOV/PUBLIC_BODIES", rgbToHex(237, 141, 24))
colorMap.set("OTHER", rgbToHex(137, 166, 121))
const datasetFunding = dataSetKey.map(function (entry) {
// const randomColor = getRandomColor();
const color: string = colorMap.get(entry[0])!;
console.log(color)
return {
backgroundColor: color,
label: entry[0] + "(" + entry[1] + ")",//composition+year
data: labelsNREN.map(nren => dataPerCompositionPerYear(entry[1], nren, entry[0])),
stack: entry[1],
borderRadius: 10,
borderSkipped: false,
barPercentage: 0.5,
borderWidth: 0.5,
categoryPercentage: 0.8
}
})
function dataPerCompositionPerYear(year: number, nren: string, composition: string) {
let compValue = ""
fsResponse.find(function (entry, index) {
if (entry.YEAR == year && entry.NREN == nren) {
if (composition === "CLIENT INSTITUTIONS")
compValue = String(entry.CLIENT_INSTITUTIONS);
if (composition === "COMMERCIAL")
compValue = entry.COMMERCIAL;
if (composition === "EUROPEAN FUNDING")
compValue = entry.EUROPEAN_FUNDING;
if (composition === "GOV/PUBLIC_BODIES")
compValue = entry.GOV_PUBLIC_BODIES;
if (composition === "OTHER")
compValue = entry.OTHER;
}
})
console.log(compValue)
return compValue;
useEffect(() => {
if (fundingSourceData != undefined) {
const dataset = createFundingSourceDataset(fundingSourceData);
setDataset(dataset);
}
console.log(datasetFunding)
}, [fundingSourceData])
const dataResponse: FundingGraphMatrix = {
// datasets: datasetFunding,
datasets: datasetFunding,
labels: labelsNREN.map(l => l.toString())
}
setFundingMatrixResponse(dataResponse);
}
const empty_bar_response = {
datasets: [
{
backgroundColor: '',
data: [],
label: '',
borderRadius: 0,
borderSkipped: false,
barPercentage: 0,
borderWidth: 0,
stack: '0',
categoryPercentage: 0.5
}],
labels: []
}
const fundingAPIResponse: FundingGraphMatrix = fundingMatrixResponse !== undefined
? fundingMatrixResponse : empty_bar_response;
const dataset: FundingSourceDataset = fundingSourceDataset ?? EMPTY_DATASET;
return (
<div className='center' >
<div className="chart-container" style={{ position: 'relative', height: '300vh', 'width': '80vw' }}>
<h1>Income Source</h1>
<Bar data={fundingAPIResponse}
//height={200}
options={{
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
labels: {
boxWidth: 20,
boxHeight: 30,
pointStyle: "rectRounded",
borderRadius: 6,
useBorderRadius: true,
<h1 >Funding Source</h1>
},
},
},
scales: {
x: {
stacked: true,
ticks: {
callback: (value: string | number) => {
if (typeof value === 'number') {
return value.toFixed(2);
}
return value;
},
},
},
y: {
stacked: true,
},
},
indexAxis: "y",
}}
></Bar>
<div className="chart-container" style={{ 'minHeight': '100vh', 'width': '60vw', }}>
<Bar
data={dataset}
options={chartOptions}
/>
</div>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment