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

Finished release 0.29.

parents a64c3a17 ede0b06b
Branches
Tags 0.29
No related merge requests found
Showing
with 1997 additions and 1365 deletions
......@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
## [0.29] - 2023-08-20
- More fixes to the survey model
- Fix small bugs in the progress bars
- Update the surveyjs version because it fixes a bug with the validation not jumping to the right question
- When editing the survey and clicking the back button now also gives a warning message if you stay on the survey website
- The conversion for prefill data now also includes data for the service matrix questions
- The next button is visible again to help with navigation
- The tooltips in the service matrix section are improved
- A completed survey can now not be saved anymore without validation
## [0.28] - 2023-08-16
- Added migration that updates all surveys to the fixed version
- Correctly handle read-only/edit mode (especially when user cannot edit)
......
......@@ -4,5 +4,6 @@ include compendium_v2/templates/survey-index.html
recursive-include compendium_v2/migrations/versions *
include compendium_v2/migrations/alembic.ini
recursive-include compendium_v2/background_task/xlsx *
include compendium_v2/conversion/NREN-Services-prefills_2023_Recovered.xlsx
include compendium_v2/routes/survey_model.json
recursive-exclude test *
This diff is collapsed.
......@@ -54,7 +54,8 @@
"react-chartjs-2": "^5.1.0",
"react-dom": "^18.2.0",
"react-icons": "4.8.0",
"react-router-dom": "^6.5.0"
"react-router-dom": "^6.5.0",
"xlsx": "^0.18.5"
},
"description": "## development environment",
"main": "index.js",
......
import React, { ReactElement, useState } from "react";
import { BrowserRouter as Router, Routes, Route,} from "react-router-dom";
import React, { ReactElement } from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Landing from "./pages/Landing";
import ExternalPageNavBar from "./shared/ExternalPageNavBar";
import GeantFooter from "./components/global/GeantFooter";
......@@ -8,42 +8,40 @@ import CompendiumData from "./pages/CompendiumData";
import FundingSourcePage from "./pages/FundingSource";
import ChargingStructurePage from "./pages/ChargingStructure";
import StaffGraph from "./pages/StaffGraph";
import { FilterSelection } from "./Schema";
import SubOrganisation from "./pages/SubOrganisation";
import ParentOrganisation from "./pages/ParentOrganisation";
import ECProjects from "./pages/ECProjects";
import Providers from "./Providers";
import PolicyPage from "./pages/Policy";
const router = createBrowserRouter([
{ path: "/budget", element: <BudgetPage />},
{ path: "/funding", element: <FundingSourcePage />},
{ path: "/data/employment", element: <StaffGraph />},
{ path: "/data/roles", element: <StaffGraph roles />},
{ path: "/charging", element: <ChargingStructurePage />},
{ path: "/suborganisations", element: <SubOrganisation />},
{ path: "/parentorganisation", element: <ParentOrganisation />},
{ path: "/ec-projects", element: <ECProjects />},
{ path: "/policy", element: <PolicyPage />},
{ path: "/data", element: <CompendiumData />},
{ path: "*", element: <Landing />},
]);
function App(): ReactElement {
const [filterSelection, setFilterSelection] = useState<FilterSelection>({ selectedYears: [], selectedNrens: [] });
if (process.env.NODE_ENV === 'production') {
window.location.replace('/survey')
return <></>;
}
return (
<div className="app">
<Router>
<Providers>
<ExternalPageNavBar />
<Routes>
<Route path="/budget" element={<BudgetPage 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="/policy" element={<PolicyPage filterSelection={filterSelection} setFilterSelection={setFilterSelection} />} />
<Route path="/data" element={<CompendiumData />} />
<Route path="*" element={<Landing />} />
</Routes>
</Providers>
<GeantFooter />
</Router>
<Providers>
<ExternalPageNavBar />
<RouterProvider router={router} />
</Providers>
<GeantFooter />
</div>
);
......
......@@ -2,13 +2,16 @@ import React, { ReactElement } from "react";
import SidebarProvider from "./helpers/SidebarProvider";
import UserProvider from "./shared/UserProvider";
import FilterSelectionProvider from "./helpers/FilterSelectionProvider";
function Providers({ children }): ReactElement {
return (
<SidebarProvider>
<UserProvider>
{children}
<FilterSelectionProvider>
{children}
</FilterSelectionProvider>
</UserProvider>
</SidebarProvider>
);
......
import React from 'react';
import * as XLSX from "xlsx";
import {ExportType} from "../helpers/constants";
interface DownloadCSVProps {
interface DownloadProps {
data: any[];
filename?: string;
filename: string;
exportType: ExportType;
}
......@@ -36,12 +40,48 @@ function convertToCSV(jsonData: any[]): string {
return [header.join(','), ...rows].join('\r\n');
}
function convertToExcel(jsonData: any[], sheetName = "Sheet1"): Blob {
const ws = XLSX.utils.json_to_sheet(jsonData);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, sheetName);
const wbout = XLSX.write(wb, {bookType: 'xlsx', type: 'binary'});
const buffer = new ArrayBuffer(wbout.length);
const view = new Uint8Array(buffer);
for (let i = 0; i < wbout.length; i++) {
// Convert each character of the binary workbook string to an 8-bit integer and store in the Uint8Array 'view' for blob creation.
view[i] = wbout.charCodeAt(i) & 0xFF;
}
return new Blob([buffer], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'});
}
const DownloadDataButton: React.FC<DownloadProps> = ({data, filename, exportType}) => {
const downloadData = () => {
let convertedData;
let fileType;
let contentType ;
switch (exportType) {
case ExportType.EXCEL: {
convertedData = convertToExcel(data);
fileType = 'xlsx';
contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8';
break;
}
case ExportType.CSV:
default: {
convertedData = convertToCSV(data);
fileType = 'csv';
contentType = 'text/csv;charset=UTF-8';
break;
}
}
const DownloadCSVButton: React.FC<DownloadCSVProps> = ({ data, filename = 'data.csv' }) => {
const blob = new Blob([convertedData], {type: contentType});
filename = filename.endsWith(fileType) ? filename : `${filename}.${fileType}`;
const downloadCSV = () => {
const csv = convertToCSV(data);
const blob = new Blob([csv], { type: 'text/csv' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
......@@ -52,9 +92,9 @@ const DownloadCSVButton: React.FC<DownloadCSVProps> = ({ data, filename = 'data.
return (
<>
<button onClick={downloadCSV}>Download CSV</button>
<button onClick={downloadData}>Download {exportType}</button>
</>
);
}
export default DownloadCSVButton;
export default DownloadDataButton;
import React from 'react';
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { Container, Row, ButtonToolbar, Button } from 'react-bootstrap';
import { Sections } from '../helpers/constants';
......@@ -8,48 +8,44 @@ interface inputProps {
}
const SectionNavigation = ({ activeCategory }: inputProps) => {
const navigate = useNavigate();
return (
<Container>
<Row>
<ButtonToolbar className="navbox-bar gap-2 m-3">
<Link to={activeCategory === Sections.Organisation ? '.' : '/funding'}>
<Button
variant={'nav-box'}
active={activeCategory === Sections.Organisation}>
<span>{Sections.Organisation}</span>
</Button>
</Link>
<Link to={activeCategory === Sections.Policy ? '.' : '/policy'}>
<Button
variant={'nav-box'}
active={activeCategory === Sections.Policy}>
<span>{Sections.Policy}</span>
</Button>
</Link>
<Link to={activeCategory === Sections.ConnectedUsers ? '.' : '.'}>
<Button
variant={'nav-box'}
active={activeCategory === Sections.ConnectedUsers}
disabled={true}>
<span>{Sections.ConnectedUsers}</span>
</Button>
</Link>
<Link to={activeCategory === Sections.Network ? '.' : '.'}>
<Button
variant={'nav-box'}
active={activeCategory === Sections.Network}
disabled={true}>
<span>{Sections.Network}</span>
</Button>
</Link>
<Link to={activeCategory === Sections.Services ? '.' : '.'}>
<Button
variant={'nav-box'}
active={activeCategory === Sections.Services}
disabled={true}>
<span>{Sections.Services}</span>
</Button>
</Link>
<Button
onClick={() => navigate(activeCategory === Sections.Organisation ? '.' : '/funding')}
variant={'nav-box'}
active={activeCategory === Sections.Organisation}>
<span>{Sections.Organisation}</span>
</Button>
<Button
onClick={() => navigate(activeCategory === Sections.Policy ? '.' : '/policy')}
variant={'nav-box'}
active={activeCategory === Sections.Policy}>
<span>{Sections.Policy}</span>
</Button>
<Button
onClick={() => navigate(activeCategory === Sections.ConnectedUsers ? '.' : '.')}
variant={'nav-box'}
active={activeCategory === Sections.ConnectedUsers}
disabled={true}>
<span>{Sections.ConnectedUsers}</span>
</Button>
<Button
onClick={() => navigate(activeCategory === Sections.Network ? '.' : '.')}
variant={'nav-box'}
active={activeCategory === Sections.Network}
disabled={true}>
<span>{Sections.Network}</span>
</Button>
<Button
onClick={() => navigate(activeCategory === Sections.Services ? '.' : '.')}
variant={'nav-box'}
active={activeCategory === Sections.Services}
disabled={true}>
<span>{Sections.Services}</span>
</Button>
</ButtonToolbar>
</Row>
</Container>
......
import React, { Dispatch, SetStateAction, createContext, useState } from 'react';
import { FilterSelection } from '../Schema';
interface Props {
children: React.ReactNode;
}
const FilterSelectionContext = createContext<{
filterSelection: FilterSelection;
setFilterSelection: Dispatch<SetStateAction<FilterSelection>>;
}>({
filterSelection: { selectedYears: [], selectedNrens: [] },
setFilterSelection: () => { }
});
const FilterSelectionProvider: React.FC<Props> = ({ children }) => {
const [filterSelection, setFilterSelection] = useState<FilterSelection>({ selectedYears: [], selectedNrens: [] });
return (
<FilterSelectionContext.Provider value={{ filterSelection, setFilterSelection }}>
{children}
</FilterSelectionContext.Provider>
);
};
export { FilterSelectionContext };
export default FilterSelectionProvider;
\ No newline at end of file
......@@ -4,4 +4,9 @@ export enum Sections {
ConnectedUsers = 'CONNECTED USERS',
Network = 'NETWORK',
Services = 'SERVICES',
}
export enum ExportType {
CSV = "CSV",
EXCEL = "EXCEL",
}
\ No newline at end of file
import React, { ReactElement, useEffect, useMemo, useState } from 'react';
import React, { ReactElement, useContext, useEffect, useMemo, useState } from 'react';
import { Row } from "react-bootstrap";
import { Budget, FilterSelection } from "../Schema";
import { Budget } from "../Schema";
import { createBudgetDataset, getYearsAndNrens, loadDataWithFilterNrenSelectionFallback } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter";
import LineGraph from "../components/graphing/LineGraph";
import { Sections } from '../helpers/constants';
import DownloadCSVButton from "../components/DownloadCSVButton";
import {ExportType, Sections} from '../helpers/constants';
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function BudgetPage({ filterSelection, setFilterSelection }: inputProps): ReactElement {
function BudgetPage(): ReactElement {
const [budgetResponse, setBudget] = useState<Budget[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { nrens } = useMemo(
() => getYearsAndNrens(budgetResponse),
......@@ -46,7 +43,8 @@ function BudgetPage({ filterSelection, setFilterSelection }: inputProps): ReactE
fluctuation of budget over years and with other NRENs.' category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={budgetResponse} filename="budget_data.csv"/>
<DownloadDataButton data={budgetResponse} filename="budget_data.csv" exportType={ExportType.CSV}/>
<DownloadDataButton data={budgetResponse} filename="budget_data.xlsx" exportType={ExportType.EXCEL}/>
</Row>
<Row>
<LineGraph data={budgetData} />
......
import React, { useMemo, useState } from "react";
import React, { useContext, useMemo, useState } from "react";
import {Row, Table} from "react-bootstrap";
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
import { ChargingStructure, FilterSelection } from "../Schema";
import { ChargingStructure } from "../Schema";
import { createChargingStructureDataLookup, getYearsAndNrens, loadDataWithFilterSelectionFallback } from "../helpers/dataconversion";
import ColorPill from "../components/ColorPill";
import DataPage from "../components/DataPage";
import Filter from "../components/graphing/Filter";
import { Sections } from "../helpers/constants";
import DownloadCSVButton from "../components/DownloadCSVButton";
import {ExportType, Sections} from "../helpers/constants";
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from "../helpers/FilterSelectionProvider";
ChartJS.register(
CategoryScale,
......@@ -20,13 +20,9 @@ ChartJS.register(
Legend
);
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function ChargingStructurePage({ filterSelection, setFilterSelection }: inputProps): React.ReactElement {
function ChargingStructurePage(): React.ReactElement {
const [chargingStructureData, setChargingStructureData] = useState<ChargingStructure[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(chargingStructureData),
......@@ -58,7 +54,8 @@ function ChargingStructurePage({ filterSelection, setFilterSelection }: inputPro
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={chargingStructureData} filename="charging_mechanism_of_nrens_per_year.csv"/>
<DownloadDataButton data={chargingStructureData} filename="charging_mechanism_of_nrens_per_year.csv" exportType={ExportType.CSV}/>
<DownloadDataButton data={chargingStructureData} filename="charging_mechanism_of_nrens_per_year.xlsx" exportType={ExportType.EXCEL} />
</Row>
<Table className="charging-struct-table" striped bordered responsive>
<colgroup>
......
import React, { useEffect, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import {Row, Table} from "react-bootstrap";
import { ECProject, FilterSelection } from "../Schema";
import { ECProject } from "../Schema";
import { createECProjectsDataLookup, getYearsAndNrens, loadDataWithFilterSelectionFallback } from '../helpers/dataconversion';
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import { Sections } from '../helpers/constants';
import DownloadCSVButton from "../components/DownloadCSVButton";
import {ExportType, Sections} from '../helpers/constants';
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function getJSXFromMap(data: Map<string, Map<number, ECProject[]>>) {
return Array.from(data.entries()).map(([nren, nrenMap]) => {
return Array.from(nrenMap.entries()).map(([year, projects], yearIndex) => (
......@@ -32,8 +28,9 @@ function getJSXFromMap(data: Map<string, Map<number, ECProject[]>>) {
})
}
function ECProjects({ filterSelection, setFilterSelection }: inputProps) {
function ECProjects() {
const [projectData, setProjectData] = useState<ECProject[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(projectData),
......@@ -62,7 +59,8 @@ function ECProjects({ filterSelection, setFilterSelection }: inputProps) {
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={projectData} filename="nren_involvement_in_european_commission_projects.csv"/>
<DownloadDataButton data={projectData} filename="nren_involvement_in_european_commission_projects.csv" exportType={ExportType.CSV}/>
<DownloadDataButton data={projectData} filename="nren_involvement_in_european_commission_projects.xlsx" exportType={ExportType.EXCEL}/>
</Row>
<Table borderless className='compendium-table'>
<thead>
......
import React, { useEffect, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { Col, Row } from "react-bootstrap";
import { Chart as ChartJS } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { FundingSource, FilterSelection } from "../Schema";
import { FundingSource } from "../Schema";
import { createFundingSourceDataset, getYearsAndNrens, loadDataWithFilterSelectionFallback } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import { Sections } from '../helpers/constants';
import {ExportType, Sections} from '../helpers/constants';
import ColorBadge from '../components/ColorBadge';
import DownloadCSVButton from "../components/DownloadCSVButton";
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
export const chartOptions = {
maintainAspectRatio: false,
......@@ -104,14 +105,9 @@ function FundingSourceLegend() {
);
}
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function FundingSourcePage({ filterSelection, setFilterSelection }: inputProps) {
function FundingSourcePage() {
const [fundingSourceData, setFundingSourceData] = useState<FundingSource[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(fundingSourceData),
......@@ -157,7 +153,8 @@ function FundingSourcePage({ filterSelection, setFilterSelection }: inputProps)
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={fundingSourceData} filename="income_source_of_nren_per_year.csv"/>
<DownloadDataButton data={fundingSourceData} filename="income_source_of_nren_per_year.csv" exportType={ExportType.CSV}/>
<DownloadDataButton data={fundingSourceData} filename="income_source_of_nren_per_year.xlsx" exportType={ExportType.EXCEL}/>
</Row>
<div>
<FundingSourceLegend/>
......
import React, { useEffect, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import {Row, Table} from "react-bootstrap";
import { Organisation, FilterSelection } from "../Schema";
import { Organisation } from "../Schema";
import { createOrganisationDataLookup, getYearsAndNrens, loadDataWithFilterSelectionFallback } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import { Sections } from '../helpers/constants';
import DownloadCSVButton from "../components/DownloadCSVButton";
import {ExportType, Sections} from '../helpers/constants';
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
function getJSXFromMap(data: Map<string, Map<number, Organisation[]>>) {
......@@ -20,14 +21,9 @@ function getJSXFromMap(data: Map<string, Map<number, Organisation[]>>) {
))
})
}
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function ParentOrganisation({ filterSelection, setFilterSelection }: inputProps) {
function ParentOrganisation() {
const [organisationData, setOrganisationData] = useState<Organisation[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(organisationData),
......@@ -57,7 +53,8 @@ function ParentOrganisation({ filterSelection, setFilterSelection }: inputProps)
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={organisationData} filename="nren_parent_organisations.csv"/>
<DownloadDataButton data={organisationData} filename="nren_parent_organisations.csv" exportType={ExportType.CSV}/>
<DownloadDataButton data={organisationData} filename="nren_parent_organisations.xlsx" exportType={ExportType.EXCEL}/>
</Row>
<Table borderless className='compendium-table'>
<thead>
......
import React, { useEffect, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { Table } from "react-bootstrap";
import { Policy, FilterSelection } from "../Schema";
import { Policy } from "../Schema";
import { createPolicyDataLookup, getYearsAndNrens, loadDataWithFilterSelectionFallback } from '../helpers/dataconversion';
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import { Sections } from '../helpers/constants';
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
function getJSXFromMap(data: Map<string, Map<number, Policy>>) {
const policies = [
......@@ -46,8 +42,9 @@ function getJSXFromMap(data: Map<string, Map<number, Policy>>) {
})
}
function PolicyPage({ filterSelection, setFilterSelection }: inputProps) {
function PolicyPage() {
const [policyData, setProjectData] = useState<Policy[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(policyData),
......
import React, { useEffect, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
import { NrenStaff, FilterSelection } from "../Schema";
import { NrenStaff } from "../Schema";
import { createNRENStaffDataset, getYearsAndNrens, loadDataWithFilterSelectionFallback } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import { Sections } from '../helpers/constants';
import {ExportType, Sections} from '../helpers/constants';
import WithLegend from '../components/WithLegend';
import htmlLegendPlugin from '../plugins/HTMLLegendPlugin';
import {Row} from "react-bootstrap";
import DownloadCSVButton from "../components/DownloadCSVButton";
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
ChartJS.register(
CategoryScale,
......@@ -101,13 +102,12 @@ const chartOptions = {
};
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
roles?: boolean
}
function StaffGraph({ filterSelection, setFilterSelection, roles = false }: inputProps) {
function StaffGraph({ roles = false }: inputProps) {
const [staffData, setStaffData] = useState<NrenStaff[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(staffData),
......@@ -153,14 +153,15 @@ function StaffGraph({ filterSelection, setFilterSelection, roles = false }: inpu
? "The graph shows the roles of NREN employees. On hovering over the graph will give the percentage of employees in that role. This graph can be used to compare, selecting multiple NRENs to see the fluctuation of roles over selected year and with other NRENs."
: "The graph shows the types of employment for NREN employees. On hovering over the graphs will give the percentage of employees in that type of employment. This graph can be used to compare, selecting multiple NRENs to see the fluctuation of types of employment over selected year and with other NRENs.";
const filename = roles ? "roles_of_nren_employees.csv" : "types_of_employment_for_nren.csv";
const filename = roles ? "roles_of_nren_employees" : "types_of_employment_for_nrens";
return (
<DataPage title={title}
description={description}
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={staffData} filename={filename}/>
<DownloadDataButton data={staffData} filename={filename} exportType={ExportType.CSV}/>
<DownloadDataButton data={staffData} filename={filename} exportType={ExportType.EXCEL}/>
</Row>
<WithLegend>
<div className="chart-container" style={{'height': `${height}rem`}}>
......
import React, { useEffect, useMemo, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import {Row, Table} from "react-bootstrap";
import { Organisation, FilterSelection } from "../Schema";
import { Organisation } from "../Schema";
import { createOrganisationDataLookup, getYearsAndNrens, loadDataWithFilterSelectionFallback } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import { Sections } from '../helpers/constants';
import DownloadCSVButton from "../components/DownloadCSVButton";
import {ExportType, Sections} from '../helpers/constants';
import DownloadDataButton from "../components/DownloadDataButton";
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
function getJSXFromMap(data: Map<string, Map<number, Organisation[]>>) {
......@@ -27,13 +28,9 @@ function getJSXFromMap(data: Map<string, Map<number, Organisation[]>>) {
})
}
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
}
function SubOrganisation({ filterSelection, setFilterSelection }: inputProps) {
function SubOrganisation() {
const [organisationData, setOrganisationData] = useState<Organisation[]>([]);
const {filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { years, nrens } = useMemo(
() => getYearsAndNrens(organisationData),
......@@ -62,7 +59,8 @@ function SubOrganisation({ filterSelection, setFilterSelection }: inputProps) {
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadCSVButton data={organisationData} filename="nren_suborganisations.csv"/>
<DownloadDataButton data={organisationData} filename="nren_suborganisations.csv" exportType={ExportType.CSV}/>
<DownloadDataButton data={organisationData} filename="nren_suborganisations.xlsx" exportType={ExportType.EXCEL}/>
</Row>
<Table borderless className='compendium-table'>
<thead>
......
File added
......@@ -5,12 +5,15 @@ conversion
This module loads the survey data from 2022 from the survey database
and stores the data in the json structure of the new survey, so that
it can be used to prefill the 2023 survey.
It also loads an xlsx file with the data for the services questions.
"""
import logging
import click
import json
import os
import openpyxl
from sqlalchemy import delete, text, select
import compendium_v2
......@@ -27,6 +30,8 @@ setup_logging()
logger = logging.getLogger('conversion')
EXCEL_FILE = os.path.join(os.path.dirname(__file__), "NREN-Services-prefills_2023_Recovered.xlsx")
def query_nren(nren_id: int):
query = mapping.ANSWERS_2022_QUERY.format(nren_id)
......@@ -106,7 +111,46 @@ def convert_answers(answers):
return {"data": data}
def load_service_data():
wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True, read_only=True)
ws = wb["Sheet1"]
rows = list(ws.rows)
titles = rows[0]
nren_service_data_columns = {}
for i in range(0, 131):
if titles[i].value:
name = titles[i].value.split(' ')[0].upper()
name = {'KIFÜ': 'KIFU', 'AZSCIENCENET': 'ANAS', 'PSNC': 'PIONIER'}.get(name, name)
nren_service_data_columns[name] = i
nren_service_data = {}
for nren_name, start_column in nren_service_data_columns.items():
nren_service_data[nren_name] = {}
for row_index in range(2, 61):
row = rows[row_index]
service_name = row[0].value
if row[start_column].value and row[start_column].value.upper() == 'YES':
question_data = {"offered": ["yes"]}
if row[start_column + 1].value:
question_data["name"] = row[start_column + 1].value
if row[start_column + 2].value:
question_data["additional_information"] = row[start_column + 2].value
question_key_base = mapping.SERVICES_MAPPING[service_name]
question_name, subquestion_name = question_key_base.split(':')
question_dict = nren_service_data[nren_name].setdefault(question_name, {})
question_dict[subquestion_name] = question_data
return nren_service_data
def _cli(app):
nren_service_data = load_service_data()
with app.app_context():
nren_surveys = {}
......@@ -115,6 +159,10 @@ def _cli(app):
survey_db_nren_id = mapping.NREN_IDS[nren.name]
nren_surveys[nren] = query_nren(survey_db_nren_id)
for nren_name in nren_service_data.keys():
if nren_name not in [n.name.upper() for n in nren_surveys.keys()]:
raise Exception('NREN in excel not found in source dataset!')
db.session.execute(delete(SurveyResponse).where(
SurveyResponse.survey_year == 2022
))
......@@ -123,6 +171,7 @@ def _cli(app):
for nren, answers in nren_surveys.items():
survey_dict = convert_answers(answers)
survey_dict["data"].update(nren_service_data.get(nren.name.upper(), {}))
survey_dict["page"] = 0
survey_dict["verification_status"] = {}
response = SurveyResponse(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment