Skip to content
Snippets Groups Projects
Commit 538048a9 authored by Remco Tukker's avatar Remco Tukker
Browse files

Merge branch 'develop' into feature/COMP-170_part_2_introduce_flask_sqlalchemy

parents 6e62575f b7fe7e0f
Branches
Tags
1 merge request!18use flask-sqlalchemy for the main db
Showing
with 545 additions and 309 deletions
......@@ -2,6 +2,9 @@
All notable changes to this project will be documented in this file.
## [0.14] - 2023-05-03
- Added a navigation sidebar to Organization pages
## [0.13] - 2023-05-02
- Added the staff graph pages
- Updated the filter drop down boxes
......
......@@ -121,7 +121,7 @@ def fetch_charging_structure_excel_data():
ws = wb[sheet_name]
# iterate over the rows in the worksheet
def create_points_for_year(start_row, end_row, year, col_start):
def create_points_for_2021(start_row, end_row, year, col_start):
for row in range(start_row, end_row):
# extract the data from the row
nren = ws.cell(row=row, column=col_start).value
......@@ -145,11 +145,38 @@ def fetch_charging_structure_excel_data():
yield nren.upper(), year, charging_structure
def create_points_for_2019(start_row, end_row, year, col_start):
for row in range(start_row, end_row):
# extract the data from the row
nren = ws.cell(row=row, column=col_start).value
charging_structure = ws.cell(row=row, column=col_start + 1).value
structure_2 = ws.cell(row=row, column=col_start + 2).value
logger.info(f'NREN: {nren}, Charging Structure: {charging_structure}, {structure_2}, Year: {year}')
if charging_structure is not None:
if structure_2 not in [None, '']:
charging_structure = FeeType.other
elif "do not charge" in charging_structure:
charging_structure = FeeType.no_charge
elif "combination" in charging_structure:
charging_structure = FeeType.combination
elif "flat" in charging_structure:
charging_structure = FeeType.flat_fee
elif "usage-based" in charging_structure:
charging_structure = FeeType.usage_based_fee
elif "Other" in charging_structure:
charging_structure = FeeType.other
else:
charging_structure = None
logger.info(f'NREN: {nren}, Charging Structure: {charging_structure}, Year: {year}')
yield nren.upper(), year, charging_structure
# For 2021
yield from create_points_for_year(3, 46, 2021, 2)
yield from create_points_for_2021(3, 46, 2021, 2)
# For 2019
yield from create_points_for_year(3, 46, 2019, 6)
yield from create_points_for_2019(3, 46, 2019, 6)
def fetch_staffing_excel_data():
......
"""restore primary keys for budgets, charging_structure, and funding_source tables
Revision ID: 35a343afaf83
Revises: 2edefbaa9db4
Create Date: 2023-05-03 16:37:20.781024
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = '35a343afaf83'
down_revision = '2edefbaa9db4'
branch_labels = None
depends_on = None
def upgrade():
op.execute('DELETE FROM budgets;')
op.execute('DELETE FROM charging_structure;')
op.execute('DELETE FROM funding_source;')
op.create_primary_key(None, 'budgets', ['nren_id', 'year'])
op.create_primary_key(None, 'charging_structure', ['nren_id', 'year'])
op.create_primary_key(None, 'funding_source', ['nren_id', 'year'])
def downgrade():
op.drop_constraint('pk_funding_source', 'funding_source')
op.drop_constraint('pk_charging_structure', 'charging_structure')
op.drop_constraint('pk_budgets', 'budgets')
......@@ -154,6 +154,7 @@ def transfer_budget(nren_dict):
budget_entry = model.BudgetEntry(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
budget=budget,
year=2022,
)
......@@ -192,6 +193,7 @@ def transfer_funding_sources(nren_dict):
funding_source = model.FundingSource(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
year=2022,
client_institutions=nren_info[FundingSource.CLIENT_INSTITUTIONS],
european_funding=nren_info[FundingSource.EUROPEAN_FUNDING],
......@@ -240,6 +242,7 @@ def transfer_staff_data(nren_dict):
f' ({employed} != {technical})')
staff_data = model.NrenStaff(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
year=2022,
permanent_fte=nren_info[StaffQuestion.PERMANENT_FTE],
......@@ -270,6 +273,7 @@ def transfer_nren_parent_org(nren_dict):
continue
parent_org = model.ParentOrganization(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
year=2022,
organization=value,
......@@ -315,6 +319,7 @@ def transfer_nren_sub_org(nren_dict):
for nren_name, suborgs in lookup.items():
for suborg_name, role in suborgs:
suborg = model.SubOrganization(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
year=2022,
organization=suborg_name,
......@@ -348,6 +353,7 @@ def transfer_charging_structure(nren_dict):
charging_structure = None
charging_structure = model.ChargingStructure(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
year=2022,
fee_type=charging_structure,
......@@ -388,6 +394,7 @@ def transfer_ec_projects(nren_dict):
val = val.split('(contract n')[0]
ec_project = model.ECProject(
nren=nren_dict[nren_name],
nren_id=nren_dict[nren_name].id,
year=2022,
project=str(val).strip()
......
......@@ -44,6 +44,7 @@ def db_budget_migration(nren_dict):
budget_entry = model.BudgetEntry(
nren=nren_dict[abbrev],
nren_id=nren_dict[abbrev].id,
budget=float(budget.budget),
year=year
)
......@@ -60,7 +61,12 @@ def db_budget_migration(nren_dict):
if budget > 200:
logger.warning(f'{nren} has budget set to >200M EUR for {year}. ({budget})')
budget_entry = model.BudgetEntry(nren=nren_dict[abbrev], budget=budget, year=year)
budget_entry = model.BudgetEntry(
nren=nren_dict[abbrev],
nren_id=nren_dict[abbrev].id,
budget=budget,
year=year
)
db.session.merge(budget_entry)
db.session.commit()
......@@ -85,6 +91,7 @@ def db_funding_migration(nren_dict):
budget_entry = model.FundingSource(
nren=nren_dict[abbrev],
nren_id=nren_dict[abbrev].id,
year=year,
client_institutions=client_institution,
european_funding=european_funding,
......@@ -105,7 +112,11 @@ def db_charging_structure_migration(nren_dict):
continue
charging_structure_entry = model.ChargingStructure(
nren=nren_dict[abbrev], year=year, fee_type=charging_structure)
nren=nren_dict[abbrev],
nren_id=nren_dict[abbrev].id,
year=year,
fee_type=charging_structure
)
db.session.merge(charging_structure_entry)
db.session.commit()
......
This diff is collapsed.
......@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name='compendium-v2',
version="0.14",
version="0.15",
author='GEANT',
author_email='swd@geant.org',
description='Flask and React project for displaying '
......
import React, { ReactElement, useState } from "react";
import React, { ReactElement, useState, useContext } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Landing from "./pages/Landing";
import ExternalPageNavBar from "./components/global/ExternalPageNavBar";
import GeantFooter from "./components/global/GeantFooter";
import DataAnalysis from "./pages/DataAnalysis";
import AnnualReport from "./pages/AnnualReport";
import CompendiumData from "./pages/CompendiumData";
import FundingSourcePage from "./pages/FundingSource";
import ChargingStructurePage from "./pages/ChargingStructure";
......@@ -13,32 +12,34 @@ import { FilterSelection } from "./Schema";
import SubOrganisation from "./pages/SubOrganisation";
import ParentOrganisation from "./pages/ParentOrganisation";
import ECProjects from "./pages/ECProjects";
import SidebarProvider from "./helpers/SidebarProvider";
function App(): ReactElement {
const [filterSelection, setFilterSelection] = useState<FilterSelection>({ selectedYears: [], selectedNrens: [] });
return (
<div className="app">
<Router>
<ExternalPageNavBar />
<Routes>
<Route path="/data" element={<CompendiumData />} />
<Route path="/analysis" element={
<DataAnalysis filterSelection={filterSelection} setFilterSelection={setFilterSelection} />}
/>
<Route path="/report" element={<AnnualReport />} />
<Route path="/funding" element={<FundingSourcePage filterSelection={filterSelection} setFilterSelection={setFilterSelection} />}
/>
<Route path="/data/employment" element={<StaffGraph filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/data/roles" element={<StaffGraph roles filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/charging" element={<ChargingStructurePage filterSelection={filterSelection} setFilterSelection={setFilterSelection}/>} />
<Route path="/suborganisations" element={<SubOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/parentorganisation" element={<ParentOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/ec-projects" element={<ECProjects filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="*" element={<Landing />} />
</Routes>
<SidebarProvider>
<Routes>
<Route path="/analysis" element={<DataAnalysis filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/funding" element={<FundingSourcePage filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/data/employment" element={<StaffGraph filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/data/roles" element={<StaffGraph roles filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/charging" element={<ChargingStructurePage filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/suborganisations" element={<SubOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/parentorganisation" element={<ParentOrganisation filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/ec-projects" element={<ECProjects filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/data" element={<CompendiumData />} />
<Route path="*" element={<Landing />} />
</Routes>
</SidebarProvider>
<GeantFooter />
</Router>
......
import React from 'react';
import { Link } from 'react-router-dom';
import { Row } from 'react-bootstrap';
import Sidebar from './SideBar';
const OrganizationSidebar = () => {
return (
<Sidebar>
<Row>
<Link to="/analysis" className="link-text-underline">
<span>Budget of NRENs per Year</span>
</Link>
</Row>
<Row>
<Link to="/funding" className="link-text-underline">
<span>Income Source of NRENs per Year</span>
</Link>
</Row>
<Row>
<Link to="/charging" className="link-text-underline">
<span>Charging Mechanism of NRENs per Year</span>
</Link>
</Row>
<Row>
<Link to="/data/roles" className="link-text-underline">
<span>Roles of NREN employees</span>
</Link>
</Row>
<Row>
<Link to="/data/employment" className="link-text-underline">
<span>Types of employment for NRENs</span>
</Link>
</Row>
<Row>
<Link to="/suborganisations" className="link-text-underline">
<span>NREN Suborganisations</span>
</Link>
</Row>
<Row>
<Link to="/parentorganisation" className="link-text-underline">
<span>NREN Parent Organisations</span>
</Link>
</Row>
<Row>
<Link to="/ec-projects" className="link-text-underline">
<span>NREN Involvement in European Commission Projects</span>
</Link>
</Row>
</Sidebar>
)
}
export default OrganizationSidebar
\ No newline at end of file
import React, { useContext } from 'react';
import { sidebarContext } from "../helpers/SidebarProvider";
interface Props {
children: React.ReactNode;
}
const Sidebar: React.FC<Props> = ({ children }) => {
const { show, toggle } = useContext(sidebarContext);
return (
<div className="sidebar-wrapper">
<nav className={show ? '' : 'no-sidebar'} id="sidebar">
<div className={`menu-items`}>{children}</div>
</nav>
<button className="toggle-btn" onClick={toggle}>
{show ? 'Hide Sidebar' : 'Show Sidebar'}
</button>
</div>
);
};
export default Sidebar;
\ No newline at end of file
import React, { createContext, useState } from 'react';
interface Props {
children: React.ReactNode;
}
const sidebarContext = createContext<{
show: boolean;
toggle: React.Dispatch<any>;
}>({
show: false,
toggle: () => { }
});
const SidebarProvider: React.FC<Props> = ({ children }) => {
const [show, setShow] = useState<boolean>(false);
const toggle = () => {
setShow(!show);
};
return (
<sidebarContext.Provider value={{ show, toggle }}>
{children}
</sidebarContext.Provider>
);
};
export { sidebarContext };
export default SidebarProvider;
\ No newline at end of file
import React, { ReactElement } from 'react';
function AnnualReport(): ReactElement {
return (
<div>
<h1>Annual Report</h1>
</div>
);
}
export default AnnualReport;
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { Container, Row, Col, Table } from "react-bootstrap";
import Chart, { ChartOptions, Plugin, ChartTypeRegistry, ScatterDataPoint } from 'chart.js';
import { Bubble, Scatter } from "react-chartjs-2";
import { ChargingStructure, ChargingStructureDataset, FilterSelection } from "../Schema";
import { ChargingStructure, FilterSelection } from "../Schema";
import {
Chart as ChartJS,
......@@ -11,12 +9,13 @@ import {
BarElement,
Title,
Tooltip,
Legend,
scales,
Legend
} from 'chart.js';
import Filter from "../components/graphing/Filter";
import { createChargingStructureDataLookup } from "../helpers/dataconversion";
import ColorPill from "../components/ColorPill";
import OrganizationSidebar from "../components/OrganizationSidebar";
ChartJS.register(
CategoryScale,
LinearScale,
......@@ -26,10 +25,6 @@ ChartJS.register(
Legend
);
const EMPTY_DATASET = { datasets: [], labels: [] };
async function getData(): Promise<ChargingStructure[]> {
try {
const response = await fetch('/api/charging/');
......@@ -61,89 +56,90 @@ function ChargingStructurePage({ filterSelection, setFilterSelection }: inputPro
return (
<>
<Container fluid className="p-0">
<Row>
<h1>Charging Structure</h1>
</Row>
<Row>
<Col xs={9}>
<Table className="charging-struct-table" striped bordered responsive >
<colgroup>
<col span={1} style={{ width: "10%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
</colgroup>
<thead>
<tr >
<th></th>
<th>Flat fee based on bandwidth</th>
<th>Usage based fee</th>
<th>Combination flat fee & usage basedfee </th>
<th>No Direct Charge</th>
<th>Other</th>
</tr>
</thead>
<tbody>
{filterSelection.selectedNrens
.sort((a, b) => a>b? 1 : -1)
.map((nren: string) => (
<tr key={nren}>
<td>{nren}</td>
<td >
{filterSelection.selectedYears
.sort((a, b) => a>b? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"flat_fee"}/>
))}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a>b? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"usage_based_fee"}/>
))
}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a>b? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"combination"}/>
))}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a>b? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"no_charge"}/>
))}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a>b? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"other"}/>
))}
</td>
</tr>
<OrganizationSidebar />
<Container fluid className="p-0">
<Row>
<h1>Charging Structure</h1>
</Row>
<Row>
<Col xs={9}>
<Table className="charging-struct-table" striped bordered responsive >
<colgroup>
<col span={1} style={{ width: "10%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
<col span={1} style={{ width: "18%" }} />
</colgroup>
<thead>
<tr >
<th></th>
<th>Flat fee based on bandwidth</th>
<th>Usage based fee</th>
<th>Combination flat fee & usage basedfee </th>
<th>No Direct Charge</th>
<th>Other</th>
</tr>
</thead>
<tbody>
{filterSelection.selectedNrens
.sort((a, b) => a > b ? 1 : -1)
.map((nren: string) => (
<tr key={nren}>
<td>{nren}</td>
<td >
{filterSelection.selectedYears
.sort((a, b) => a > b ? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"flat_fee"} />
))}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a > b ? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"usage_based_fee"} />
))
}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a > b ? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"combination"} />
))}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a > b ? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"no_charge"} />
))}
</td>
<td>
{filterSelection.selectedYears
.sort((a, b) => a > b ? 1 : -1)
.map((year: number, index) => (
<ColorPill key={year} nren={nren} year={year} index={index} dataLookup={dataLookup} discriminator={"other"} />
))}
</td>
</tr>
))}
</tbody>
))}
</tbody>
</Table>
</Col>
<Col xs={3}>
<Filter
filterOptions={{availableYears: [...labelsYear], availableNrens: [...labelsNREN]}}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
</Table>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...labelsYear], availableNrens: [...labelsNREN] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
</>
);
};
......
import React, {ReactElement, useEffect, useState} from 'react';
import React, { ReactElement, useEffect, useState } from 'react';
import { Col, Container, Row } from "react-bootstrap";
import LineGraph from "../components/graphing/LineGraph";
import {Budget, FilterSelection} from "../Schema";
import Filter from "../components/graphing/Filter";
import {createBudgetDataset} from "../helpers/dataconversion";
import OrganizationSidebar from "../components/OrganizationSidebar";
import { createBudgetDataset } from "../helpers/dataconversion";
import { Budget, FilterSelection } from "../Schema";
function api<T>(url: string, options: RequestInit | undefined = undefined): Promise<T> {
return fetch(url, options)
......@@ -35,7 +36,6 @@ function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): Reac
budgetData.datasets.forEach(dataset => {
dataset.hidden = !filterSelection.selectedNrens.includes(dataset.label);
});
useEffect(() => {
const loadData = async () => {
......@@ -61,6 +61,8 @@ function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): Reac
return (
<div>
<h1>Data Analysis</h1>
<OrganizationSidebar />
<Container>
<Row>
<Col>
......@@ -68,15 +70,15 @@ function DataAnalysis({ filterSelection, setFilterSelection }: inputProps): Reac
<LineGraph data={budgetData} />
</Row>
<Row>
The numbers are based on 30 NRENs that reported their budgets continuously throughout this
period. This means that some larger NRENs are not included and therefore the actual total budgets will
have been higher. (For comparison, the total budget according to the 2021 survey results based on the data
for all responding NRENs that year is €555 M). The percentage change is based on the previous year&apos;s budget.
The numbers are based on 30 NRENs that reported their budgets continuously throughout this
period. This means that some larger NRENs are not included and therefore the actual total budgets will
have been higher. (For comparison, the total budget according to the 2021 survey results based on the data
for all responding NRENs that year is €555 M). The percentage change is based on the previous year&apos;s budget.
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{availableYears: [], availableNrens: [...nrens]}}
filterOptions={{ availableYears: [], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
......
import React, { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row, Table } from "react-bootstrap";
import Filter from "../components/graphing/Filter"
import OrganizationSidebar from '../components/OrganizationSidebar';
import {
ECProject,
......@@ -123,41 +124,45 @@ function ECProjects({ filterSelection, setFilterSelection }: inputProps) {
}, [setFilterSelection]);
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Involvement in European Commission Projects</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>EC Project Membership</th>
</tr>
</thead>
<tbody className='table-bg-highlighted'>
{getJSXFromMap(projectDataByYear)}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<div className="sticky-top" style={{ top: '1rem' }}>
{/* the sticky-top class makes the filter follow the user as they scroll down the page */}
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</div>
</Col>
</Row>
</Container>
<>
<OrganizationSidebar />
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Involvement in European Commission Projects</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>EC Project Membership</th>
</tr>
</thead>
<tbody className='table-bg-highlighted'>
{getJSXFromMap(projectDataByYear)}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<div className="sticky-top" style={{ top: '1rem' }}>
{/* the sticky-top class makes the filter follow the user as they scroll down the page */}
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</div>
</Col>
</Row>
</Container>
</>
);
}
export default ECProjects;
import React, {useEffect, useMemo, useState} from 'react';
import {Col, Container, Row} from "react-bootstrap";
import React, { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row } from "react-bootstrap";
import { Bar } from 'react-chartjs-2';
import { createFundingSourceDataset } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import OrganizationSidebar from "../components/OrganizationSidebar";
import {
Chart as ChartJS,
......@@ -19,6 +21,7 @@ import {
} from "../Schema";
ChartJS.register(
CategoryScale,
LinearScale,
......@@ -82,7 +85,7 @@ function getYearsAndNrens(sourceData: FundingSource[]) {
years.add(datapoint.YEAR);
nrens.add(datapoint.NREN);
});
return {years: years, nrens: nrens};
return { years: years, nrens: nrens };
}
interface inputProps {
......@@ -132,30 +135,34 @@ function FundingSourcePage({ filterSelection, setFilterSelection }: inputProps)
}, [setFilterSelection]);
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h1>Funding Source</h1>
</Row>
<Row>
<div className="chart-container" style={{ 'minHeight': '100vh', 'width': '60vw', }}>
<Bar
data={fundingSourceDataset}
options={chartOptions}
/>
</div>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{availableYears: [...years], availableNrens: [...nrens]}}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
<>
<OrganizationSidebar />
<Container>
<Row>
<Col xs={9}>
<Row>
<h1>Funding Source</h1>
</Row>
<Row>
<div className="chart-container" style={{ 'minHeight': '100vh', 'width': '60vw', }}>
<Bar
data={fundingSourceDataset}
options={chartOptions}
/>
</div>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
</>
);
}
export default FundingSourcePage;
......@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row, Table } from "react-bootstrap";
import { createOrganisationDataLookup } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import OrganizationSidebar from '../components/OrganizationSidebar';
import {
Organisation,
......@@ -69,48 +70,52 @@ function ParentOrganisation({ filterSelection, setFilterSelection }: inputProps)
}
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Parent Organisations</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Parent Organisation</th>
</tr>
</thead>
<tbody>
{Array.from(organisationDataset).sort().map(([_, nrenEntry]) => (
Array.from(nrenEntry).sort().map(([_, nrenDataList], yearIndex) => (
nrenDataList.sort(sortOrganisation).map((nrenData, nrenDataIndex) => (
<tr key={nrenData.nren + nrenData.year + nrenData.name}>
<td>{yearIndex == 0 && nrenDataIndex == 0 && nrenData.nren}</td>
<td>{nrenDataIndex == 0 && nrenData.year}</td>
<td>{nrenData.name}</td>
</tr>
))
)
)
<>
<OrganizationSidebar />
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Parent Organisations</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Parent Organisation</th>
</tr>
</thead>
<tbody>
{Array.from(organisationDataset).sort().map(([_, nrenEntry]) => (
Array.from(nrenEntry).sort().map(([_, nrenDataList], yearIndex) => (
nrenDataList.sort(sortOrganisation).map((nrenData, nrenDataIndex) => (
<tr key={nrenData.nren + nrenData.year + nrenData.name}>
<td>{yearIndex == 0 && nrenDataIndex == 0 && nrenData.nren}</td>
<td>{nrenDataIndex == 0 && nrenData.year}</td>
<td>{nrenData.name}</td>
</tr>
))
)
)
))}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
</>
))}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
);
}
export default ParentOrganisation;
......@@ -3,6 +3,8 @@ import { Col, Container, Row } from "react-bootstrap";
import { Bar } from 'react-chartjs-2';
import { createNRENStaffDataset } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import OrganizationSidebar from "../components/OrganizationSidebar";
import {
Chart as ChartJS,
......@@ -141,31 +143,35 @@ function StaffGraph({ filterSelection, setFilterSelection, roles = false }: inpu
const text = roles ? "Roles" : "Employment";
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Staff {text}</h3>
</Row>
<Row>
<div className="chart-container" style={{ 'minHeight': '60vh', 'width': '60vw', }}>
<Bar
data={nrenStaffDataset}
options={chartOptions}
/>
</div>
</Row>
</Col>
<Col xs={3}>
<Filter
max1year
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
<>
<OrganizationSidebar />
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Staff {text}</h3>
</Row>
<Row>
<div className="chart-container" style={{ 'minHeight': '60vh', 'width': '60vw', }}>
<Bar
data={nrenStaffDataset}
options={chartOptions}
/>
</div>
</Row>
</Col>
<Col xs={3}>
<Filter
max1year
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
</>
);
}
export default StaffGraph;
......@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
import { Col, Container, Row, Table } from "react-bootstrap";
import { createOrganisationDataLookup } from "../helpers/dataconversion";
import Filter from "../components/graphing/Filter"
import OrganizationSidebar from '../components/OrganizationSidebar';
import {
Organisation,
......@@ -69,50 +70,54 @@ function SubOrganisation({ filterSelection, setFilterSelection }: inputProps) {
}
return (
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Suborganisations</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Suborganisation</th>
<th>Role</th>
</tr>
</thead>
<tbody>
{Array.from(organisationDataset).sort().map(([_, nrenEntry]) => (
Array.from(nrenEntry).sort().map(([_, nrenDataList], yearIndex) => (
nrenDataList.sort(sortOrganisation).map((nrenData, nrenDataIndex) => (
<tr key={nrenData.nren + nrenData.year + nrenData.name}>
<td>{yearIndex == 0 && nrenDataIndex == 0 && nrenData.nren}</td>
<td>{nrenDataIndex == 0 && nrenData.year}</td>
<td>{nrenData.name}</td>
<td>{nrenData.role}</td>
</tr>
))
)
)
<>
<OrganizationSidebar />
<Container>
<Row>
<Col xs={9}>
<Row>
<h3>NREN Suborganisations</h3>
</Row>
<Row>
<Table>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Suborganisation</th>
<th>Role</th>
</tr>
</thead>
<tbody>
{Array.from(organisationDataset).sort().map(([_, nrenEntry]) => (
Array.from(nrenEntry).sort().map(([_, nrenDataList], yearIndex) => (
nrenDataList.sort(sortOrganisation).map((nrenData, nrenDataIndex) => (
<tr key={nrenData.nren + nrenData.year + nrenData.name}>
<td>{yearIndex == 0 && nrenDataIndex == 0 && nrenData.nren}</td>
<td>{nrenDataIndex == 0 && nrenData.year}</td>
<td>{nrenData.name}</td>
<td>{nrenData.role}</td>
</tr>
))
)
)
))}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
</>
))}
</tbody>
</Table>
</Row>
</Col>
<Col xs={3}>
<Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
</Col>
</Row>
</Container>
);
}
export default SubOrganisation;
.sidebar-wrapper {
display: flex;
position: fixed;
top: calc(50vh - 10%);
.menu-items {
padding: 10px;
}
}
.sidebar-wrapper>nav {
transition: 0.25s;
// transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
transition-property: margin-left;
margin-left: 0;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
a {
text-decoration: none;
}
a:hover {
background-color: $yellow-orange;
text-decoration: none;
}
}
nav.no-sidebar {
margin-left: -79%;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment