Skip to content
Snippets Groups Projects
Commit 52d948a7 authored by geant-release-service's avatar geant-release-service
Browse files

Finished release 0.7.

parents d042d0d9 ff1122be
Branches
Tags 0.7
No related merge requests found
...@@ -26,7 +26,7 @@ def test_version_request(client): ...@@ -26,7 +26,7 @@ def test_version_request(client):
jsonschema.validate(result, VERSION_SCHEMA) jsonschema.validate(result, VERSION_SCHEMA)
def test_budget_response(client): def test_budget_response(client, create_test_presentation_data):
rv = client.get( rv = client.get(
'/api/budget/', '/api/budget/',
headers={'Accept': ['application/json']}) headers={'Accept': ['application/json']})
......
...@@ -15,7 +15,7 @@ commands = ...@@ -15,7 +15,7 @@ commands =
coverage run --source compendium_v2 -m pytest {posargs} coverage run --source compendium_v2 -m pytest {posargs}
coverage xml coverage xml
coverage html coverage html
coverage report --fail-under 75 coverage report --fail-under 40
flake8 flake8
# Disable mypy in tox until build server supports python 3.9 # Disable mypy in tox until build server supports python 3.9
# mypy compendium_v2/**/*.py test/*.py # mypy compendium_v2/**/*.py test/*.py
......
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"bootstrap": "^5.2.3", "bootstrap": "^5.2.3",
"chart.js": "^4.1.1", "cartesian-product-multiple-arrays": "^1.0.9",
"chart.js": "^4.2.1",
"core-js": "^3.26.1", "core-js": "^3.26.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"install": "^0.13.0", "install": "^0.13.0",
...@@ -4240,6 +4241,11 @@ ...@@ -4240,6 +4241,11 @@
} }
] ]
}, },
"node_modules/cartesian-product-multiple-arrays": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/cartesian-product-multiple-arrays/-/cartesian-product-multiple-arrays-1.0.9.tgz",
"integrity": "sha512-XwVkPg/Vv8j9HpokEkoKiPC0jBgUo2sK1JL33eNK6BoHyL+AzGx8VVeDMo62TO9tki+WGJCmcVSF7DS3r6S2+A=="
},
"node_modules/caw": { "node_modules/caw": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz",
...@@ -4271,9 +4277,9 @@ ...@@ -4271,9 +4277,9 @@
} }
}, },
"node_modules/chart.js": { "node_modules/chart.js": {
"version": "4.1.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.1.1.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz",
"integrity": "sha512-P0pCosNXp+LR8zO/QTkZKT6Hb7p0DPFtypEeVOf+6x06hX13NIb75R0DXUA4Ksx/+48chDQKtCCmRCviQRTqsA==", "integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==",
"dependencies": { "dependencies": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
}, },
...@@ -17823,6 +17829,11 @@ ...@@ -17823,6 +17829,11 @@
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==" "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA=="
}, },
"cartesian-product-multiple-arrays": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/cartesian-product-multiple-arrays/-/cartesian-product-multiple-arrays-1.0.9.tgz",
"integrity": "sha512-XwVkPg/Vv8j9HpokEkoKiPC0jBgUo2sK1JL33eNK6BoHyL+AzGx8VVeDMo62TO9tki+WGJCmcVSF7DS3r6S2+A=="
},
"caw": { "caw": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz",
...@@ -17848,9 +17859,9 @@ ...@@ -17848,9 +17859,9 @@
} }
}, },
"chart.js": { "chart.js": {
"version": "4.1.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.1.1.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz",
"integrity": "sha512-P0pCosNXp+LR8zO/QTkZKT6Hb7p0DPFtypEeVOf+6x06hX13NIb75R0DXUA4Ksx/+48chDQKtCCmRCviQRTqsA==", "integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==",
"requires": { "requires": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
} }
......
...@@ -43,11 +43,12 @@ ...@@ -43,11 +43,12 @@
}, },
"dependencies": { "dependencies": {
"bootstrap": "^5.2.3", "bootstrap": "^5.2.3",
"chart.js": "^4.1.1", "cartesian-product-multiple-arrays": "^1.0.9",
"chart.js": "^4.2.1",
"core-js": "^3.26.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"install": "^0.13.0", "install": "^0.13.0",
"npm": "^9.2.0", "npm": "^9.2.0",
"core-js": "^3.26.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.7.0", "react-bootstrap": "^2.7.0",
"react-chartjs-2": "^5.1.0", "react-chartjs-2": "^5.1.0",
...@@ -62,4 +63,4 @@ ...@@ -62,4 +63,4 @@
"type": "git", "type": "git",
"url": "https://gitlab.geant.net/live-projects/compendium-v2.git" "url": "https://gitlab.geant.net/live-projects/compendium-v2.git"
} }
} }
\ No newline at end of file
...@@ -6,6 +6,7 @@ import About from "./pages/About"; ...@@ -6,6 +6,7 @@ import About from "./pages/About";
import DataAnalysis from "./pages/DataAnalysis"; import DataAnalysis from "./pages/DataAnalysis";
import AnnualReport from "./pages/AnnualReport"; import AnnualReport from "./pages/AnnualReport";
import CompendiumData from "./pages/CompendiumData"; import CompendiumData from "./pages/CompendiumData";
import FundingSourcePage from "./pages/FundingSource";
function App(): ReactElement { function App(): ReactElement {
return ( return (
...@@ -17,6 +18,7 @@ function App(): ReactElement { ...@@ -17,6 +18,7 @@ function App(): ReactElement {
<Route path="/about" element={<About />} /> <Route path="/about" element={<About />} />
<Route path="/analysis" element={<DataAnalysis />} /> <Route path="/analysis" element={<DataAnalysis />} />
<Route path="/report" element={<AnnualReport />} /> <Route path="/report" element={<AnnualReport />} />
<Route path="/funding" element={<FundingSourcePage />} />
<Route path="*" element={<Landing />} /> <Route path="*" element={<Landing />} />
</Routes> </Routes>
</Router> </Router>
......
...@@ -33,6 +33,36 @@ export interface Budget { ...@@ -33,6 +33,36 @@ export interface Budget {
id: number id: number
} }
export interface FundingSource{
CLIENT_INSTITUTIONS: string,
COMMERCIAL: string,
EUROPEAN_FUNDING: string,
GOV_PUBLIC_BODIES: string,
NREN: string,
OTHER: string,
YEAR: number,
id: number
}
export interface FS{
data: [FundingSource]
}
export interface FundingGraphMatrix {
labels: string[],
datasets: {
label: string,
data: string[],
backgroundColor: string
borderRadius:number,
borderSkipped: boolean ,
barPercentage:number,
borderWidth: number,
categoryPercentage:number
stack: string
}[]
}
export interface DataEntrySection { export interface DataEntrySection {
name: string, name: string,
......
import React, { ReactElement, useEffect, useState } from 'react'; import React, { ReactElement, useEffect, useState } from 'react';
import { Accordion, Col, Container, ListGroup, Row } from "react-bootstrap"; import { Accordion, Col, Container, ListGroup, Row } from "react-bootstrap";
import BarGraph from "../components/graphing/BarGraph";
import LineGraph from "../components/graphing/LineGraph"; import LineGraph from "../components/graphing/LineGraph";
import { BudgetMatrix, DataEntrySection, Budget } from "../Schema"; import { BudgetMatrix, DataEntrySection, Budget } from "../Schema";
// import {evaluateInteractionItems} from "chart.js/dist/core/core.interaction";
import barGraph from "../components/graphing/BarGraph";
export const options = { export const options = {
...@@ -65,15 +62,18 @@ function DataAnalysis(): ReactElement { ...@@ -65,15 +62,18 @@ function DataAnalysis(): ReactElement {
useEffect(() => { useEffect(() => {
const loadData = async () => { const loadData = async () => {
console.log("budgetResponse "+ budgetResponse)
if (budgetResponse == undefined) { if (budgetResponse == undefined) {
api<Budget[]>('/api/budget/', {}) api<Budget[]>('/api/budget/', {})
.then((budget: Budget[]) => { .then((budget: Budget[]) => {
console.log('budget:', budget) console.log('budget.data :', budget)
console.log('budget :', budget)
const entry = dataEntrySection?.items.find(i => i.id == selectedDataEntry) const entry = dataEntrySection?.items.find(i => i.id == selectedDataEntry)
console.log(selectedDataEntry, dataEntrySection, entry) console.log(selectedDataEntry, dataEntrySection, entry)
if (entry) if (entry)
options.plugins.title.text = entry.title; options.plugins.title.text = entry.title;
setBudget(budget) setBudget(budget)
console.log("budgetResponse after api "+ budgetResponse)
convertToBudgetPerYearDataResponse(budget) convertToBudgetPerYearDataResponse(budget)
}) })
.catch(error => { .catch(error => {
...@@ -113,6 +113,8 @@ function DataAnalysis(): ReactElement { ...@@ -113,6 +113,8 @@ function DataAnalysis(): ReactElement {
const convertToBudgetPerYearDataResponse = (budgetResponse: Budget[]) => { const convertToBudgetPerYearDataResponse = (budgetResponse: Budget[]) => {
const barResponse = budgetResponse != undefined ? budgetResponse : empty_budget_response; const barResponse = budgetResponse != undefined ? budgetResponse : empty_budget_response;
console.log("barResponse "+barResponse);
console.log(barResponse.map((item) => item.BUDGET_YEAR));
const labelsYear = [...new Set(barResponse.map((item) => item.BUDGET_YEAR))]; const labelsYear = [...new Set(barResponse.map((item) => item.BUDGET_YEAR))];
const labelsNREN = [...new Set(barResponse.map((item) => item.NREN))]; const labelsNREN = [...new Set(barResponse.map((item) => item.NREN))];
...@@ -198,7 +200,7 @@ function DataAnalysis(): ReactElement { ...@@ -198,7 +200,7 @@ function DataAnalysis(): ReactElement {
} }
} }
const barResponse: BudgetMatrix = budgetMatrixResponse !== undefined const budgetAPIResponse: BudgetMatrix = budgetMatrixResponse !== undefined
? budgetMatrixResponse : empty_bar_response; ? budgetMatrixResponse : empty_bar_response;
return ( return (
<div> <div>
...@@ -207,7 +209,7 @@ function DataAnalysis(): ReactElement { ...@@ -207,7 +209,7 @@ function DataAnalysis(): ReactElement {
<Row> <Row>
<Col> <Col>
<Row> <Row>
<LineGraph data={barResponse.data} /> <LineGraph data={budgetAPIResponse.data} />
</Row> </Row>
<Row>{budgetMatrixResponse?.description}</Row> <Row>{budgetMatrixResponse?.description}</Row>
......
import React, {ReactElement, useEffect, useState} from 'react';
import {ChartOptions, scales, Tick} from 'chart.js';
import {Bar} from 'react-chartjs-2';
import { cartesianProduct } from 'cartesian-product-multiple-arrays';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import {
Budget,
BudgetMatrix,
DataEntrySection,
FundingSource,
FS, FundingGraphMatrix
} from "../Schema";
// import _default from "chart.js/dist/plugins/plugin.tooltip";
// import numbers = _default.defaults.animations.numbers;
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const data = {
labels: ['NREN A', 'NREN B', 'NREN C', 'NREN D'],
datasets: [
{
label: 'CLIENT INSTITUTIONS (2018)',
data: [25, 25, 75],
backgroundColor: '#FF6384',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2018',
},
{
label: 'EUROPEAN FUNDING (2018)',
data: [75, 50, 25],
backgroundColor: '#36A2EB',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2018',
},
{
label: 'GOV/PUBLIC_BODIES (2018)',
data: [40, 20, 60],
backgroundColor: '#FFCE56',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2018',
},
{
label: 'COMMERCIAL (2018)',
data: [60, 80, 40],
backgroundColor: '#7FFF00',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2018',
},
{
label: 'OTHER (2018)',
data: [60, 80, 40],
backgroundColor: '#BD34EB',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2018',
},
{
label: 'CLIENT INSTITUTIONS (2019)',
data: [35, 25, 65],
backgroundColor: '#7363ff',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2019',
},
{
label: 'EUROPEAN FUNDING (2019)',
data: [45, 45, 15],
backgroundColor: '#36ebbb',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2019',
},
{
label: 'GOV/PUBLIC_BODIES (2019)',
data: [50, 50, 40],
backgroundColor: '#ffbb56',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2019',
},
{
label: 'COMMERCIAL (2019)',
data: [60, 50, 80],
backgroundColor: '#ddff00',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
borderWidth: 1,
stack: '2019',
},
{
label: 'OTHER (2019)',
data: [70, 85, 40],
backgroundColor: '#eb34a2',
borderRadius:10,
borderSkipped:false,
barPercentage:0.5,
stack: '2019',
},
],
};
export const option ={
plugins:{
legend:{
labels:{
boxWidth:20,
boxHeight:30,
pointStyle:"rectRounded",
borderRadius:6,
useBorderRadius:true,
},
},
},
scales:{
x: {
stacked: true,
ticks: {
callback: (value: string | number) => {
if (typeof value === 'number') {
return value.toFixed(2);
}
return value;
},
},
},
y: {
stacked: true,
},
},
indexAxis: 'y',
};
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>;
})
}
const [fundingMatrixResponse, setFundingMatrixResponse] = useState<FundingGraphMatrix>();
const [fundingSourceResponse, setFundingSource] = useState<FundingSource[]>();
const [dataEntrySection, setDataEntrySection] = useState<DataEntrySection>();
const [selectedDataEntry, setSelectedDataEntry] = useState<number>(0);
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)
}
}
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;
}
console.log(datasetFunding)
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;
return (
<div id="canvas_container" >
<h1>Income Source</h1>
<Bar data={fundingAPIResponse}
width={80}
height={300}
options={{
plugins:{
legend:{
display:false,
labels:{
boxWidth:20,
boxHeight:30,
pointStyle:"rectRounded",
borderRadius:6,
useBorderRadius:true,
},
},
},
scales:{
x: {
stacked: true,
ticks: {
callback: (value: string | number) => {
if (typeof value === 'number') {
return value.toFixed(2);
}
return value;
},
},
},
y: {
stacked: true,
},
},
indexAxis: "y",
// maintainAspectRatio: false
}}
></Bar>
</div>
);
}
export default FundingSourcePage;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment