diff --git a/compendium_v2/db/survey.py b/compendium_v2/db/survey.py
index 90e6d101eb1a12bebb9d1820bb6692a9d3633226..dfe57ab7eb45a948ca3deaecba357e9ffa2724a2 100644
--- a/compendium_v2/db/survey.py
+++ b/compendium_v2/db/survey.py
@@ -54,3 +54,43 @@ def get_budget_by_year():
response_data['datasets'].append(dataset)
return response_data
+
+
+def get_budget_by_nren():
+ budget_data = db_survey.session.execute(
+ db_survey.select(AnnualBudgetEntry)
+ .filter(AnnualBudgetEntry.region_name == 'Western Europe')
+ ).scalars()
+
+ annual_data = {
+
+ }
+ seen_years = set()
+
+ for line_item in budget_data:
+ li_year = line_item.year
+ li_country_code = line_item.country_code
+ if li_country_code not in annual_data:
+ annual_data[li_country_code] = {}
+ seen_years.add(li_year)
+ annual_data[li_country_code][li_year] = line_item.budget
+
+ sorted_years = sorted(seen_years)
+ response_data = {
+ 'labels': sorted_years,
+ 'datasets': []
+ }
+
+ for country in sorted(annual_data.keys()):
+ dataset = {
+ 'label': country,
+ 'data': []
+ }
+ for year in sorted_years:
+ budget_amount = annual_data[country].get(year)
+ dataset['data'].append(float(budget_amount)
+ if budget_amount else None)
+
+ response_data['datasets'].append(dataset)
+
+ return response_data
diff --git a/compendium_v2/routes/data_entry.py b/compendium_v2/routes/data_entry.py
index c2a03b59dd2505ce3f94fa16636d6db7ca42e036..f38e8f62ed1ae880895b7c6baa4736f2c12703d9 100644
--- a/compendium_v2/routes/data_entry.py
+++ b/compendium_v2/routes/data_entry.py
@@ -5,7 +5,7 @@ from flask import Blueprint, abort, jsonify, url_for
from compendium_v2.db import db
from compendium_v2.db.models import (DataEntryItem, DataEntrySection,
DataSourceType)
-from compendium_v2.db.survey import get_budget_by_year
+from compendium_v2.db.survey import get_budget_by_nren, get_budget_by_year
from compendium_v2.routes import common
routes = Blueprint('data-entry', __name__)
@@ -131,11 +131,14 @@ def load_data(data_source_id: DataSourceType) -> Any:
response_data = {}
if data_source_id == DataSourceType.BUDGETS_BY_YEAR:
response_data = get_budget_by_year()
+ if data_source_id == DataSourceType.BUDGETS_BY_NREN:
+ response_data = get_budget_by_nren()
# Enrich response data
# Add the colour formatting
for index, dataset in enumerate(response_data['datasets']):
dataset['backgroundColor'] = col_pal[index % len(col_pal)]
+ dataset['borderColor'] = col_pal[index % len(col_pal)]
return response_data
diff --git a/setup.py b/setup.py
index 6ceebadc047cfa2ec283de6e8737f7452ee4e9a6..49655b8eae643d3dd20afc46f51bdd080adec869 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name='compendium-v2',
- version="0.3",
+ version='0.3',
author='GEANT',
author_email='swd@geant.org',
description='Flask and React project for displaying '
diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx
index 8abd76294b6d89bf4f5a22e1816e95b329d801de..c945027a18785c3002d931423f68ea47134b0fe3 100644
--- a/webapp/src/App.tsx
+++ b/webapp/src/App.tsx
@@ -1,8 +1,8 @@
import React, {ReactElement} from 'react';
-import AnnualReport from './AnnualReport';
-import DataAnalysis from './DataAnalysis';
+import AnnualReport from './pages/AnnualReport';
+import DataAnalysis from './pages/DataAnalysis';
import Navigation from './Navigation';
-import About from './About';
+import About from './pages/About';
import {BrowserRouter as Router,Switch, Route} from 'react-router-dom'
diff --git a/webapp/src/Schema.tsx b/webapp/src/Schema.tsx
index d34bfc256089a18b486b60bfc814fe39ed77b5a6..905dd1d3487017a6ca18571013a1993976a15895 100644
--- a/webapp/src/Schema.tsx
+++ b/webapp/src/Schema.tsx
@@ -26,6 +26,15 @@ export interface BudgetMatrix{
title:string
}
+export interface DataEntrySection {
+ name: string,
+ description: string,
+ items: {
+ id: number,
+ title: string,
+ url: string
+ }[]
+}
export interface Service {
compendium_id: number,
diff --git a/webapp/src/components/graphing/BarGraph.tsx b/webapp/src/components/graphing/BarGraph.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..38f49b0f65633cf74f6952c94cf571031ba10c2a
--- /dev/null
+++ b/webapp/src/components/graphing/BarGraph.tsx
@@ -0,0 +1,32 @@
+import React, {ReactElement, useEffect, useState} from 'react';
+import {Bar} from 'react-chartjs-2';
+import {DataSetProps} from './types';
+
+
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
+} from 'chart.js';
+
+
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend
+);
+
+function BarGraph(data: DataSetProps): ReactElement {
+ return (
+ <Bar data={data.data} options={{}}/>
+ );
+}
+
+export default BarGraph;
diff --git a/webapp/src/components/graphing/LineGraph.tsx b/webapp/src/components/graphing/LineGraph.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..be9d1e026838498672e334e073deadbc1938f8dc
--- /dev/null
+++ b/webapp/src/components/graphing/LineGraph.tsx
@@ -0,0 +1,46 @@
+import React, {ReactElement, useEffect, useState} from 'react';
+import {Line} from 'react-chartjs-2';
+import {DataSetProps} from './types';
+
+
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ Title,
+ Tooltip,
+ Legend,
+} from 'chart.js';
+
+
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ Title,
+ Tooltip,
+ Legend
+);
+
+const options = {
+ responsive: true,
+ plugins: {
+ legend: {
+ position: 'top' as const,
+ },
+ title: {
+ display: true,
+ text: '',
+ },
+ },
+};
+function LineGraph(data: DataSetProps): ReactElement {
+ return (
+ <Line data={data.data} options={options}/>
+ );
+}
+
+export default LineGraph;
diff --git a/webapp/src/components/graphing/types.ts b/webapp/src/components/graphing/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ebb08f1be0a0fc55689111165f301214510848d
--- /dev/null
+++ b/webapp/src/components/graphing/types.ts
@@ -0,0 +1,14 @@
+export type DatasetEntry = {
+ backgroundColor: string,
+ borderColor?: string,
+ data: (number|null)[],
+ label: string
+
+}
+
+export type DataSetProps = {
+ data: {
+ datasets: DatasetEntry[],
+ labels: string[]
+ }
+}
diff --git a/webapp/src/About.tsx b/webapp/src/pages/About.tsx
similarity index 100%
rename from webapp/src/About.tsx
rename to webapp/src/pages/About.tsx
diff --git a/webapp/src/AnnualReport.tsx b/webapp/src/pages/AnnualReport.tsx
similarity index 97%
rename from webapp/src/AnnualReport.tsx
rename to webapp/src/pages/AnnualReport.tsx
index 23c73eabefa2cd690e720fca4243aee48fe1dc05..90f744aa561c339a65605b92c6476155aa505bfd 100644
--- a/webapp/src/AnnualReport.tsx
+++ b/webapp/src/pages/AnnualReport.tsx
@@ -1,5 +1,5 @@
import React, {useState, useEffect, ReactElement} from 'react';
-import {Nren, Service, ServiceMatrix} from "./Schema";
+import {Nren, Service, ServiceMatrix} from "../Schema";
// const api_url = window.location.origin;
diff --git a/webapp/src/DataAnalysis.tsx b/webapp/src/pages/DataAnalysis.tsx
similarity index 51%
rename from webapp/src/DataAnalysis.tsx
rename to webapp/src/pages/DataAnalysis.tsx
index 2f9f41b9511aee0b603cc9cea44f925f3e0ac5e8..5030d6d0c2920bb708653082a0aff02dde63e2fa 100644
--- a/webapp/src/DataAnalysis.tsx
+++ b/webapp/src/pages/DataAnalysis.tsx
@@ -1,28 +1,12 @@
import React, {ReactElement, useEffect, useState} from 'react';
-import {Accordion, Col, Container, Form, Row} from "react-bootstrap";
-import {Bar} from 'react-chartjs-2';
-
-
-import {
- Chart as ChartJS,
- CategoryScale,
- LinearScale,
- BarElement,
- Title,
- Tooltip,
- Legend,
-} from 'chart.js';
-
-import {BudgetMatrix} from "./Schema";
-
-ChartJS.register(
- CategoryScale,
- LinearScale,
- BarElement,
- Title,
- Tooltip,
- Legend
-);
+import {Accordion, Col, Container, Form, ListGroup, Row} from "react-bootstrap";
+import BarGraph from "../components/graphing/BarGraph";
+import LineGraph from "../components/graphing/LineGraph";
+import {DataSetProps} from "../components/graphing/types";
+
+
+import {BudgetMatrix, DataEntrySection} from "../Schema";
+
export const options = {
// indexAxis: 'y' as const,
@@ -42,7 +26,6 @@ function DataAnalysis(): ReactElement {
function api<T>(url: string, options: RequestInit): Promise<T> {
return fetch(url, options)
.then((response) => {
- console.log(response)
if (!response.ok) {
return response.text().then((message) => {
console.error(`Failed to load datax: ${message}`, response.status);
@@ -55,12 +38,31 @@ function DataAnalysis(): ReactElement {
}
const [budgetResponse, setBudgetMatrix] = useState<BudgetMatrix>();
+ const [dataEntrySection, setDataEntrySection] = useState<DataEntrySection>();
+ const [selectedDataEntry, setSelectedDataEntry] = useState<number>(0);
+
// const [services, setServices] = useState<Service[][]>();
useEffect(() => {
// let timeoutRef = 0;
+ const getDataEntries = () => {
+ api<DataEntrySection>('/api/data-entries/sections/1', {
+ referrerPolicy: "unsafe-url",
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "Content-Type": "text/plain"
+ }
+ }).then( (dataEntrySectionResponse: DataEntrySection) => {
+ setDataEntrySection(dataEntrySectionResponse);
+ })
+ }
const loadData = () => {
- api<BudgetMatrix>('/api/data-entries/item/2',{
+ if (selectedDataEntry == 0) {
+ getDataEntries();
+ return;
+ }
+
+ api<BudgetMatrix>('/api/data-entries/item/' + selectedDataEntry,{
referrerPolicy: "unsafe-url",
headers: {
"Access-Control-Allow-Origin": "*",
@@ -68,15 +70,21 @@ function DataAnalysis(): ReactElement {
}
})
.then((budgetMatrix :BudgetMatrix)=>{
- console.log('got response==>data');
- console.log(budgetMatrix.data);
options.plugins.title.text = budgetMatrix.title;
setBudgetMatrix(budgetMatrix)
})
}
loadData()
- }, []);
+ }, [selectedDataEntry]);
+
+ const renderEntries = () => {
+ return (
+ <ul>
+
+ </ul>
+ );
+ };
const empty_bar_response = {
'data': {
'datasets': [
@@ -94,6 +102,25 @@ function DataAnalysis(): ReactElement {
}
const barResponse: BudgetMatrix = budgetResponse !== undefined ? budgetResponse : empty_bar_response;
+ const data: DataSetProps = {
+ data: {
+ datasets: [
+ {
+ backgroundColor: '#114466',
+ borderColor: '#114466',
+ data: [10, 13, 18,21,16,12],
+ label: '2016'
+ },
+ {
+ backgroundColor: '#FF4466',
+ borderColor: '#FF4466',
+ data: [15, null, 13,11,11,44],
+ label: '2017'
+ },
+ ],
+ labels: ['AB', 'BC', 'CD', 'DE', 'EF', 'FE']
+ }
+ }
return (
<div>
@@ -102,8 +129,10 @@ const barResponse: BudgetMatrix = budgetResponse !== undefined ? budgetResponse
<Row>
<Col>
<Row>
- <Bar data={barResponse.data} options={options}/>
-
+ <BarGraph data={barResponse.data} />
+ </Row>
+ <Row>
+ <LineGraph data={barResponse.data} />
</Row>
<Row>{budgetResponse?.description}</Row>
@@ -111,20 +140,15 @@ const barResponse: BudgetMatrix = budgetResponse !== undefined ? budgetResponse
<Col xs={3}>
<Accordion defaultActiveKey="0">
<Accordion.Item eventKey="0">
- <Accordion.Header>NRENs</Accordion.Header>
+ <Accordion.Header>Items</Accordion.Header>
<Accordion.Body>
- <Form>
- <Form.Check
- type={'checkbox'}
- label='JISC'/>
- <Form.Check
- type={'checkbox'}
- label='SURF'/>
- <Form.Check
- type={'checkbox'}
- label='URAN'/>
-
- </Form>
+ <ListGroup>
+ {
+ dataEntrySection?.items.map((item) => (
+ <ListGroup.Item key={item.id} action onClick={() => setSelectedDataEntry(item.id)}>{item.title}</ListGroup.Item>
+ ))
+ }
+ </ListGroup>
</Accordion.Body>
</Accordion.Item>
</Accordion>
diff --git a/webapp/src/styles.scss b/webapp/src/styles.scss
index 5d084e11e4766b1594c2f8d052158dbab10ca91c..85b5b785a869758b7b4b78c6c29b2282d0417432 100644
--- a/webapp/src/styles.scss
+++ b/webapp/src/styles.scss
@@ -1,5 +1,5 @@
table {
- min-width: 650;
+ min-width: 650px;
}
thead {