diff --git a/Changelog.md b/Changelog.md index 669158aca54f8a22db0a78db379c917157aef80f..f10dba2fbe98fc4242cd0d717ecbf1411c97e61b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. +## [0.36] - 2023-09-14 +- Fixed bug with sorting in user management page + ## [0.35] - 2023-09-12 - Added a publisher for the 2023 survey data - Added report pages for the traffic volume and institution urls diff --git a/MANIFEST.in b/MANIFEST.in index edff315a66901ecf2ff0533fa800de7c5fd7bd52..58874ea53d89bcd754171ef8f9c65be2a37b38ad 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,5 @@ include compendium_v2/templates/survey-index.html include compendium_v2/migrations/alembic.ini recursive-include compendium_v2/migrations/versions * recursive-include compendium_v2/migrations/surveymodels * -recursive-include compendium_v2/background_task/xlsx * -include compendium_v2/conversion/NREN-Services-prefills_2023_Recovered.xlsx +recursive-include compendium_v2/background_task/resources * recursive-exclude test * diff --git a/compendium_v2/background_task/__init__.py b/compendium_v2/background_task/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/compendium_v2/conversion/conversion.py b/compendium_v2/conversion/conversion.py index a963b4466b1347467ab28cb01f9df3322b26c65f..6da4572a93b3b07456d4acbf734df82cf81ebf23 100644 --- a/compendium_v2/conversion/conversion.py +++ b/compendium_v2/conversion/conversion.py @@ -2,7 +2,7 @@ conversion ========================= -This module loads the survey data from 2022 from the survey database +This module loads the survey data from 2022 from the old 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. @@ -21,8 +21,8 @@ from compendium_v2.environment import setup_logging from compendium_v2.db import db from compendium_v2.config import load from compendium_v2.survey_db import model as survey_model -from compendium_v2.db.model import NREN -from compendium_v2.db.survey_model import Survey, SurveyResponse, ResponseStatus +from compendium_v2.db.presentation_models import NREN +from compendium_v2.db.survey_models import Survey, SurveyResponse, ResponseStatus from compendium_v2.conversion import mapping @@ -30,7 +30,7 @@ setup_logging() logger = logging.getLogger('conversion') -EXCEL_FILE = os.path.join(os.path.dirname(__file__), "NREN-Services-prefills_2023_Recovered.xlsx") +EXCEL_FILE = os.path.join(os.path.dirname(__file__), "../resources/NREN-Services-prefills_2023_Recovered.xlsx") def query_nren(nren_id: int): diff --git a/compendium_v2/db/auth_model.py b/compendium_v2/db/auth_model.py index 50be7e3e009ef40c6762738a72b9049cf8923496..8605ab39c0173d99411e1b231c46d5371bd92450 100644 --- a/compendium_v2/db/auth_model.py +++ b/compendium_v2/db/auth_model.py @@ -15,7 +15,7 @@ from sqlalchemy.schema import ForeignKey from flask_login import UserMixin # type: ignore from compendium_v2.db import db -from compendium_v2.db.model import NREN +from compendium_v2.db.presentation_models import NREN logger = logging.getLogger(__name__) diff --git a/compendium_v2/db/model.py b/compendium_v2/db/presentation_models.py similarity index 100% rename from compendium_v2/db/model.py rename to compendium_v2/db/presentation_models.py diff --git a/compendium_v2/db/survey_model.py b/compendium_v2/db/survey_models.py similarity index 97% rename from compendium_v2/db/survey_model.py rename to compendium_v2/db/survey_models.py index 866df323a0a1bf74925246eda08a813aeca458e3..fd688d38bc4a7b8dcc32a251d93f0038ebe44dc5 100644 --- a/compendium_v2/db/survey_model.py +++ b/compendium_v2/db/survey_models.py @@ -15,7 +15,7 @@ from sqlalchemy.types import JSON from compendium_v2.db import db from compendium_v2.db.auth_model import User -from compendium_v2.db.model import NREN +from compendium_v2.db.presentation_models import NREN logger = logging.getLogger(__name__) diff --git a/compendium_v2/migrations/dump_survey_model.py b/compendium_v2/migrations/dump_survey_model.py index 314fda6dd27301fab9f57e34b1a285821b96c7d5..3869a4a6e37059c45e5b4138822008cbeed6a5b9 100644 --- a/compendium_v2/migrations/dump_survey_model.py +++ b/compendium_v2/migrations/dump_survey_model.py @@ -24,7 +24,7 @@ import compendium_v2 from compendium_v2.environment import setup_logging from compendium_v2.db import db from compendium_v2.config import load -from compendium_v2.db.survey_model import Survey +from compendium_v2.db.survey_models import Survey setup_logging() diff --git a/compendium_v2/background_task/parse_excel_data.py b/compendium_v2/publishers/excel_parser.py similarity index 98% rename from compendium_v2/background_task/parse_excel_data.py rename to compendium_v2/publishers/excel_parser.py index 69612055100efe8b73461ad84271fad9d52d98dd..1e964be8985a48695d7acf8729f94ca33581205f 100644 --- a/compendium_v2/background_task/parse_excel_data.py +++ b/compendium_v2/publishers/excel_parser.py @@ -9,8 +9,8 @@ setup_logging() logger = logging.getLogger(__name__) -EXCEL_FILE = os.path.join(os.path.dirname(__file__), "xlsx", "2021_Organisation_DataSeries.xlsx") -NETWORK_EXCEL_FILE = os.path.join(os.path.dirname(__file__), "xlsx", "2022_Networks_DataSeries.xlsx") +EXCEL_FILE = os.path.join(os.path.dirname(__file__), "../resources", "2021_Organisation_DataSeries.xlsx") +NETWORK_EXCEL_FILE = os.path.join(os.path.dirname(__file__), "../resources", "2022_Networks_DataSeries.xlsx") def fetch_budget_excel_data(): diff --git a/compendium_v2/publishers/helpers.py b/compendium_v2/publishers/helpers.py index f6492d2d93e97db681a6a93b768ef7a70c87f0d1..aeb4ecd94aa3d8b80743617fcad8d5e742abaa29 100644 --- a/compendium_v2/publishers/helpers.py +++ b/compendium_v2/publishers/helpers.py @@ -3,7 +3,7 @@ from typing import List from sqlalchemy import select -from compendium_v2.db import db, model +from compendium_v2.db import db, presentation_models URL_PATTERN = re.compile( (r'\b(https?://[^\s<>";,(){}\[\]!\\]+' @@ -17,7 +17,7 @@ def get_uppercase_nren_dict(): """ :return: a dictionary of all known NRENs db entities keyed on the uppercased name """ - current_nrens = db.session.scalars(select(model.NREN)) + current_nrens = db.session.scalars(select(presentation_models.NREN)) nren_dict = {nren.name.upper(): nren for nren in current_nrens} # add aliases that are used in the source data: nren_dict['ASNET'] = nren_dict['ASNET-AM'] diff --git a/compendium_v2/publishers/survey_publisher_v2.py b/compendium_v2/publishers/survey_publisher.py similarity index 88% rename from compendium_v2/publishers/survey_publisher_v2.py rename to compendium_v2/publishers/survey_publisher.py index 5bb12b7824d24ac3b03c6ce911578769222fbe5b..604d27235679ea8b4103e37ec1243a8e168b4bd5 100644 --- a/compendium_v2/publishers/survey_publisher_v2.py +++ b/compendium_v2/publishers/survey_publisher.py @@ -1,16 +1,28 @@ +""" +This module handles adding responses from the latest 2023 survey into the 2023 presentation_models. +The process is straightforward as the data is new and can be directly inserted into the presentation_models. + +For subsequent years like 2024, handling should be almost the same as 2023, only changes in questions +will need to be addressed. + +Usage: + Used in publish_survey API in compendium_v2/routes/survey.py +""" + from decimal import Decimal from typing import List from sqlalchemy import delete, select from compendium_v2.db import db -from compendium_v2.db.model import BudgetEntry, ChargingStructure, ECProject, ExternalConnections, FundingSource, \ - InstitutionURLs, NrenStaff, ParentOrganization, Policy, SubOrganization, TrafficVolume, ExternalConnection +from compendium_v2.db.presentation_models import BudgetEntry, ChargingStructure, ECProject, ExternalConnections, \ + InstitutionURLs, NrenStaff, ParentOrganization, Policy, SubOrganization, TrafficVolume, ExternalConnection, \ + FundingSource from compendium_v2.db.model_enums import FeeType -from compendium_v2.db.survey_model import ResponseStatus, SurveyResponse +from compendium_v2.db.survey_models import ResponseStatus, SurveyResponse -def map_2023(nren, answers) -> None: +def _map_2023(nren, answers) -> None: year = 2023 for table_class in [BudgetEntry, ChargingStructure, ECProject, FundingSource, InstitutionURLs, @@ -130,7 +142,7 @@ def publish(year): ).unique() question_mapping = { - 2023: map_2023 + 2023: _map_2023 } mapping_function = question_mapping[year] diff --git a/compendium_v2/publishers/survey_publisher_v1.py b/compendium_v2/publishers/survey_publisher_legacy_excel.py similarity index 83% rename from compendium_v2/publishers/survey_publisher_v1.py rename to compendium_v2/publishers/survey_publisher_legacy_excel.py index a6e2376f5f1f12571e6f8fefa8b89c04fb405a3e..6503b79faeda6e296c9fe2bf043658e95f4b8c5b 100644 --- a/compendium_v2/publishers/survey_publisher_v1.py +++ b/compendium_v2/publishers/survey_publisher_legacy_excel.py @@ -2,7 +2,7 @@ survey_publisher_v1 ========================= -This module loads the survey data from before 2022 from an excel file. +This module loads the survey data from before 2022 from a legacy Excel files. Missing info is filled in from the survey db for some questions. Registered as click cli command when installing compendium-v2. @@ -16,15 +16,14 @@ from sqlalchemy import select import compendium_v2 from compendium_v2.environment import setup_logging -from compendium_v2.background_task import parse_excel_data from compendium_v2.config import load -from compendium_v2.db import db, model +from compendium_v2.db import db, presentation_models from compendium_v2.survey_db import model as survey_model -from compendium_v2.publishers import helpers +from compendium_v2.publishers import helpers, excel_parser setup_logging() -logger = logging.getLogger('survey-publisher-v1') +logger = logging.getLogger('survey-publisher-legacy-excel') def db_budget_migration(nren_dict): @@ -46,7 +45,7 @@ def db_budget_migration(nren_dict): logger.warning(f'{abbrev} unknown. Skipping.') continue - budget_entry = model.BudgetEntry( + budget_entry = presentation_models.BudgetEntry( nren=nren_dict[abbrev], nren_id=nren_dict[abbrev].id, budget=float(budget.budget), @@ -55,7 +54,7 @@ def db_budget_migration(nren_dict): db.session.merge(budget_entry) # Import the data from excel sheet to database - exceldata = parse_excel_data.fetch_budget_excel_data() + exceldata = excel_parser.fetch_budget_excel_data() for abbrev, budget, year in exceldata: if abbrev not in nren_dict: @@ -65,7 +64,7 @@ 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( + budget_entry = presentation_models.BudgetEntry( nren=nren_dict[abbrev], nren_id=nren_dict[abbrev].id, budget=budget, @@ -77,7 +76,7 @@ def db_budget_migration(nren_dict): def db_funding_migration(nren_dict): # Import the data to database - data = parse_excel_data.fetch_funding_excel_data() + data = excel_parser.fetch_funding_excel_data() for (abbrev, year, client_institution, european_funding, @@ -93,7 +92,7 @@ def db_funding_migration(nren_dict): logger.warning(f'{abbrev} unknown. Skipping.') continue - budget_entry = model.FundingSource( + budget_entry = presentation_models.FundingSource( nren=nren_dict[abbrev], nren_id=nren_dict[abbrev].id, year=year, @@ -108,14 +107,14 @@ def db_funding_migration(nren_dict): def db_charging_structure_migration(nren_dict): # Import the data to database - data = parse_excel_data.fetch_charging_structure_excel_data() + data = excel_parser.fetch_charging_structure_excel_data() for (abbrev, year, charging_structure) in data: if abbrev not in nren_dict: logger.warning(f'{abbrev} unknown. Skipping.') continue - charging_structure_entry = model.ChargingStructure( + charging_structure_entry = presentation_models.ChargingStructure( nren=nren_dict[abbrev], nren_id=nren_dict[abbrev].id, year=year, @@ -126,7 +125,7 @@ def db_charging_structure_migration(nren_dict): def db_staffing_migration(nren_dict): - staff_data = parse_excel_data.fetch_staffing_excel_data() + staff_data = excel_parser.fetch_staffing_excel_data() nren_staff_map = {} for (abbrev, year, permanent_fte, subcontracted_fte) in staff_data: @@ -135,7 +134,7 @@ def db_staffing_migration(nren_dict): continue nren = nren_dict[abbrev] - nren_staff_map[(nren.id, year)] = model.NrenStaff( + nren_staff_map[(nren.id, year)] = presentation_models.NrenStaff( nren=nren, nren_id=nren.id, year=year, @@ -145,7 +144,7 @@ def db_staffing_migration(nren_dict): non_technical_fte=0 ) - function_data = parse_excel_data.fetch_staff_function_excel_data() + function_data = excel_parser.fetch_staff_function_excel_data() for (abbrev, year, technical_fte, non_technical_fte) in function_data: if abbrev not in nren_dict: logger.warning(f'{abbrev} unknown. Skipping staff function data.') @@ -156,7 +155,7 @@ def db_staffing_migration(nren_dict): nren_staff_map[(nren.id, year)].technical_fte = technical_fte nren_staff_map[(nren.id, year)].non_technical_fte = non_technical_fte else: - nren_staff_map[(nren.id, year)] = model.NrenStaff( + nren_staff_map[(nren.id, year)] = presentation_models.NrenStaff( nren=nren, nren_id=nren.id, year=year, @@ -179,40 +178,40 @@ def db_staffing_migration(nren_dict): def db_ecprojects_migration(nren_dict): - ecproject_data = parse_excel_data.fetch_ecproject_excel_data() + ecproject_data = excel_parser.fetch_ecproject_excel_data() for (abbrev, year, project) in ecproject_data: if abbrev not in nren_dict: logger.warning(f'{abbrev} unknown. Skipping.') continue nren = nren_dict[abbrev] - ecproject_entry = model.ECProject(nren=nren, nren_id=nren.id, year=year, project=project) + ecproject_entry = presentation_models.ECProject(nren=nren, nren_id=nren.id, year=year, project=project) db.session.merge(ecproject_entry) db.session.commit() def db_organizations_migration(nren_dict): - organization_data = parse_excel_data.fetch_organization_excel_data() + organization_data = excel_parser.fetch_organization_excel_data() for (abbrev, year, org) in organization_data: if abbrev not in nren_dict: logger.warning(f'{abbrev} unknown. Skipping.') continue nren = nren_dict[abbrev] - org_entry = model.ParentOrganization(nren=nren, nren_id=nren.id, year=year, organization=org) + org_entry = presentation_models.ParentOrganization(nren=nren, nren_id=nren.id, year=year, organization=org) db.session.merge(org_entry) db.session.commit() def db_traffic_volume_migration(nren_dict): - traffic_data = parse_excel_data.fetch_traffic_excel_data() + traffic_data = excel_parser.fetch_traffic_excel_data() for (abbrev, year, from_external, to_external, from_customers, to_customers) in traffic_data: if abbrev not in nren_dict: logger.warning(f'{abbrev} unknown. Skipping.') continue nren = nren_dict[abbrev] - traffic_entry = model.TrafficVolume( + traffic_entry = presentation_models.TrafficVolume( nren=nren, nren_id=nren.id, year=year, diff --git a/compendium_v2/publishers/survey_publisher_2022.py b/compendium_v2/publishers/survey_publisher_old_db_2022.py similarity index 93% rename from compendium_v2/publishers/survey_publisher_2022.py rename to compendium_v2/publishers/survey_publisher_old_db_2022.py index 7251db54c68f28e425f3b45e487a7f4d7e015329..322e06e1357b581717538a5821fbf4d02d9304c7 100644 --- a/compendium_v2/publishers/survey_publisher_2022.py +++ b/compendium_v2/publishers/survey_publisher_old_db_2022.py @@ -2,7 +2,7 @@ survey_publisher_2022 ========================= -This module loads the survey data from 2022 from the survey database. +This module loads the survey data from 2022 from the old survey database into presentation_models. Registered as click cli command when installing compendium-v2. """ @@ -22,12 +22,12 @@ from compendium_v2.environment import setup_logging from compendium_v2.config import load from compendium_v2.publishers.helpers import extract_urls from compendium_v2.survey_db import model as survey_model -from compendium_v2.db import db, model +from compendium_v2.db import db, presentation_models from compendium_v2.publishers import helpers setup_logging() -logger = logging.getLogger('survey-publisher-2022') +logger = logging.getLogger('survey-publisher-old-db-2022') BUDGET_QUERY = """ SELECT DISTINCT ON (n.id, a.question_id) @@ -189,7 +189,7 @@ def transfer_budget(nren_dict): logger.info(f'{nren_name} unknown. Skipping.') continue - budget_entry = model.BudgetEntry( + budget_entry = presentation_models.BudgetEntry( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, budget=budget, @@ -227,7 +227,7 @@ def transfer_institutions_urls(nren_dict): logger.info(f'{nren_name} has no urls for {year}. Skipping.') continue - institution_urls = model.InstitutionURLs( + institution_urls = presentation_models.InstitutionURLs( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, urls=urls, @@ -267,7 +267,7 @@ def transfer_funding_sources(nren_dict): logger.info(f'{nren_name} unknown. Skipping.') continue - funding_source = model.FundingSource( + funding_source = presentation_models.FundingSource( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=2022, @@ -303,9 +303,9 @@ def transfer_staff_data(nren_dict): for nren_name, nren_info in data.items(): if sum([nren_info[question] for question in StaffQuestion]) == 0: logger.info(f'{nren_name} has no staff data. Deleting if exists.') - db.session.execute(delete(model.NrenStaff).where( - model.NrenStaff.nren_id == nren_dict[nren_name].id, - model.NrenStaff.year == 2022 + db.session.execute(delete(presentation_models.NrenStaff).where( + presentation_models.NrenStaff.nren_id == nren_dict[nren_name].id, + presentation_models.NrenStaff.year == 2022 )) continue @@ -316,7 +316,7 @@ def transfer_staff_data(nren_dict): logger.info(f'{nren_name} FTE do not equal across employed/technical categories.' f' ({employed} != {technical})') - staff_data = model.NrenStaff( + staff_data = presentation_models.NrenStaff( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=2022, @@ -335,7 +335,9 @@ def transfer_nren_parent_org(nren_dict): 'We are affiliated to ' ] - db.session.execute(delete(model.ParentOrganization).where(model.ParentOrganization.year == 2022)) + db.session.execute(delete(presentation_models.ParentOrganization).where( + presentation_models.ParentOrganization.year == 2022) + ) rows = query_question(OrgQuestion.PARENT_ORG_NAME) for row in rows: @@ -352,7 +354,7 @@ def transfer_nren_parent_org(nren_dict): logger.info(f'{nren_name} unknown. Skipping.') continue - parent_org = model.ParentOrganization( + parent_org = presentation_models.ParentOrganization( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=2022, @@ -370,10 +372,11 @@ def transfer_nren_sub_org(nren_dict): (OrgQuestion.SUB_ORGS_4_NAME, OrgQuestion.SUB_ORGS_4_CHOICE, OrgQuestion.SUB_ORGS_4_ROLE), (OrgQuestion.SUB_ORGS_5_NAME, OrgQuestion.SUB_ORGS_5_CHOICE, OrgQuestion.SUB_ORGS_5_ROLE) ] - lookup = defaultdict(list) - db.session.execute(delete(model.SubOrganization).where(model.SubOrganization.year == 2022)) + db.session.execute(delete(presentation_models.SubOrganization).where( + presentation_models.SubOrganization.year == 2022) + ) for name, choice, role in suborg_questions: _name_rows = query_question(name) @@ -403,7 +406,7 @@ def transfer_nren_sub_org(nren_dict): for nren_name, suborgs in lookup.items(): for suborg_name, role in suborgs: - suborg = model.SubOrganization( + suborg = presentation_models.SubOrganization( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=2022, @@ -437,7 +440,7 @@ def transfer_charging_structure(nren_dict): else: charging_structure = None - charging_structure = model.ChargingStructure( + charging_structure = presentation_models.ChargingStructure( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=2022, @@ -450,7 +453,7 @@ def transfer_charging_structure(nren_dict): def transfer_ec_projects(nren_dict): # delete all existing EC projects, in case something changed db.session.execute( - delete(model.ECProject).where(model.ECProject.year == 2022) + delete(presentation_models.ECProject).where(presentation_models.ECProject.year == 2022) ) rows = query_question(ECQuestion.EC_PROJECT) @@ -478,7 +481,7 @@ def transfer_ec_projects(nren_dict): # some answers include contract numbers, which we don't want here val = val.split('(contract n')[0] - ec_project = model.ECProject( + ec_project = presentation_models.ECProject( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=2022, @@ -536,7 +539,7 @@ def transfer_policies(nren_dict): data[(nren_name, year)][question_key] = value for (nren_name, year), nren_info in data.items(): - policy_data = model.Policy( + policy_data = presentation_models.Policy( nren=nren_dict[nren_name], nren_id=nren_dict[nren_name].id, year=year, diff --git a/compendium_v2/background_task/xlsx/2021_Organisation_DataSeries.xlsx b/compendium_v2/resources/2021_Organisation_DataSeries.xlsx similarity index 100% rename from compendium_v2/background_task/xlsx/2021_Organisation_DataSeries.xlsx rename to compendium_v2/resources/2021_Organisation_DataSeries.xlsx diff --git a/compendium_v2/background_task/xlsx/2022_Connected_Users_DataSeries.xlsx b/compendium_v2/resources/2022_Connected_Users_DataSeries.xlsx similarity index 100% rename from compendium_v2/background_task/xlsx/2022_Connected_Users_DataSeries.xlsx rename to compendium_v2/resources/2022_Connected_Users_DataSeries.xlsx diff --git a/compendium_v2/background_task/xlsx/2022_Networks_DataSeries.xlsx b/compendium_v2/resources/2022_Networks_DataSeries.xlsx similarity index 100% rename from compendium_v2/background_task/xlsx/2022_Networks_DataSeries.xlsx rename to compendium_v2/resources/2022_Networks_DataSeries.xlsx diff --git a/compendium_v2/conversion/NREN-Services-prefills_2023_Recovered.xlsx b/compendium_v2/resources/NREN-Services-prefills_2023_Recovered.xlsx similarity index 100% rename from compendium_v2/conversion/NREN-Services-prefills_2023_Recovered.xlsx rename to compendium_v2/resources/NREN-Services-prefills_2023_Recovered.xlsx diff --git a/compendium_v2/routes/budget.py b/compendium_v2/routes/budget.py index 2f11be47b5661f7bf3d43dfb12722f68959926dd..b0cff93d8f4bbffcd7a9754f61392d94055c8820 100644 --- a/compendium_v2/routes/budget.py +++ b/compendium_v2/routes/budget.py @@ -3,7 +3,7 @@ from typing import Any from flask import Blueprint, jsonify -from compendium_v2.db.model import BudgetEntry +from compendium_v2.db.presentation_models import BudgetEntry from compendium_v2.routes import common diff --git a/compendium_v2/routes/charging.py b/compendium_v2/routes/charging.py index d61df556bf337dc17c3a41ae378565ec2c4a6308..8c40679b4521c945ccdc19932ad60cb6642b356b 100644 --- a/compendium_v2/routes/charging.py +++ b/compendium_v2/routes/charging.py @@ -3,7 +3,7 @@ from typing import Any from flask import Blueprint, jsonify -from compendium_v2.db.model import ChargingStructure +from compendium_v2.db.presentation_models import ChargingStructure from compendium_v2.routes import common diff --git a/compendium_v2/routes/common.py b/compendium_v2/routes/common.py index cc7b9fd1e605518f5ce60df14423fc0c28288367..be84fbfdd5dc40c4c913838be3d13a149617d9dd 100644 --- a/compendium_v2/routes/common.py +++ b/compendium_v2/routes/common.py @@ -4,7 +4,7 @@ Utilities used by multiple route blueprints. import functools import logging from compendium_v2 import db -from compendium_v2.db.model import NREN, PreviewYear +from compendium_v2.db.presentation_models import NREN, PreviewYear from flask import Response, request from sqlalchemy import select diff --git a/compendium_v2/routes/ec_projects.py b/compendium_v2/routes/ec_projects.py index aaf1c74fdbea718bf50adfe73115eee34156b225..501d2206bdf3238a738b5a71af14bea75aa3f68a 100644 --- a/compendium_v2/routes/ec_projects.py +++ b/compendium_v2/routes/ec_projects.py @@ -3,7 +3,7 @@ from typing import Any from flask import Blueprint, jsonify -from compendium_v2.db.model import ECProject +from compendium_v2.db.presentation_models import ECProject from compendium_v2.routes import common diff --git a/compendium_v2/routes/funding.py b/compendium_v2/routes/funding.py index 2ca91eb33ef9bb649c93ba5dc0d326982c692a18..0c1101900f038bc198e689d21e10962921022e9a 100644 --- a/compendium_v2/routes/funding.py +++ b/compendium_v2/routes/funding.py @@ -3,7 +3,7 @@ import logging from flask import Blueprint, jsonify from compendium_v2.routes import common -from compendium_v2.db.model import FundingSource +from compendium_v2.db.presentation_models import FundingSource from typing import Any diff --git a/compendium_v2/routes/institutions_urls.py b/compendium_v2/routes/institutions_urls.py index 7153a8fc2da1f6c6fb23b42c53a008b4a6ca9898..8af1bbb67cd51df9a922ca0be240ba4278856713 100644 --- a/compendium_v2/routes/institutions_urls.py +++ b/compendium_v2/routes/institutions_urls.py @@ -4,7 +4,7 @@ from flask import Blueprint, jsonify from sqlalchemy import select from compendium_v2.db import db -from compendium_v2.db.model import NREN, InstitutionURLs +from compendium_v2.db.presentation_models import NREN, InstitutionURLs from compendium_v2.routes import common routes = Blueprint('institutions-urls', __name__) diff --git a/compendium_v2/routes/organization.py b/compendium_v2/routes/organization.py index 473994b9a4d703769ecc71828288faf72320cc93..b39f7fd859b3a3f3261cf26f3381b03bb641fe40 100644 --- a/compendium_v2/routes/organization.py +++ b/compendium_v2/routes/organization.py @@ -3,7 +3,7 @@ from typing import Any from flask import Blueprint, jsonify -from compendium_v2.db.model import ParentOrganization, SubOrganization +from compendium_v2.db.presentation_models import ParentOrganization, SubOrganization from compendium_v2.routes import common diff --git a/compendium_v2/routes/policy.py b/compendium_v2/routes/policy.py index 68c6add03d2726792e5ef287a6a1520ad7efc27c..a97dcf224517acf82519f9f77f932d476a353735 100644 --- a/compendium_v2/routes/policy.py +++ b/compendium_v2/routes/policy.py @@ -2,7 +2,7 @@ import logging from typing import Any from flask import Blueprint, jsonify -from compendium_v2.db.model import Policy +from compendium_v2.db.presentation_models import Policy from compendium_v2.routes import common routes = Blueprint('policy', __name__) diff --git a/compendium_v2/routes/response.py b/compendium_v2/routes/response.py index 9ba078e5db8c00cf4a7bf7fc0588508b7fc603e3..1c691431e22b47634c32d9ec3095612bb16b6ba2 100644 --- a/compendium_v2/routes/response.py +++ b/compendium_v2/routes/response.py @@ -9,8 +9,8 @@ from sqlalchemy import select from sqlalchemy.orm import lazyload from compendium_v2.db import db -from compendium_v2.db.model import NREN -from compendium_v2.db.survey_model import Survey, SurveyResponse, SurveyStatus, ResponseStatus, RESPONSE_NOT_STARTED +from compendium_v2.db.presentation_models import NREN +from compendium_v2.db.survey_models import Survey, SurveyResponse, SurveyStatus, ResponseStatus, RESPONSE_NOT_STARTED from compendium_v2.routes import common from compendium_v2.auth.session_management import admin_required, User diff --git a/compendium_v2/routes/staff.py b/compendium_v2/routes/staff.py index 3c049385c9a568bde26bd37cc93dc97b4e03df2a..3a23cd6503dd12fc91ad6411cb9023372f191e70 100644 --- a/compendium_v2/routes/staff.py +++ b/compendium_v2/routes/staff.py @@ -2,7 +2,7 @@ import logging from flask import Blueprint, jsonify -from compendium_v2.db.model import NrenStaff +from compendium_v2.db.presentation_models import NrenStaff from compendium_v2.routes import common from typing import Any diff --git a/compendium_v2/routes/survey.py b/compendium_v2/routes/survey.py index 5d685e7f29789734997ff9d94e93b13d7aca0e43..35766d19748433c9c1e82a547cf6bb25011f2d7b 100644 --- a/compendium_v2/routes/survey.py +++ b/compendium_v2/routes/survey.py @@ -7,9 +7,9 @@ from sqlalchemy import delete, select from sqlalchemy.orm import joinedload, load_only from compendium_v2.db import db -from compendium_v2.db.model import NREN, PreviewYear -from compendium_v2.db.survey_model import Survey, SurveyResponse, SurveyStatus, RESPONSE_NOT_STARTED -from compendium_v2.publishers.survey_publisher_v2 import publish +from compendium_v2.db.presentation_models import NREN, PreviewYear +from compendium_v2.db.survey_models import Survey, SurveyResponse, SurveyStatus, RESPONSE_NOT_STARTED +from compendium_v2.publishers.survey_publisher import publish from compendium_v2.routes import common from compendium_v2.auth.session_management import admin_required diff --git a/compendium_v2/routes/traffic.py b/compendium_v2/routes/traffic.py index 70255d0f847be12d54b62308cdc4763e6b263dde..56668fc41cf347be32c08afece9e773244cd7417 100644 --- a/compendium_v2/routes/traffic.py +++ b/compendium_v2/routes/traffic.py @@ -3,7 +3,7 @@ import logging from flask import Blueprint, jsonify from compendium_v2.routes import common -from compendium_v2.db.model import TrafficVolume +from compendium_v2.db.presentation_models import TrafficVolume from typing import Any diff --git a/compendium_v2/routes/user.py b/compendium_v2/routes/user.py index f526e33a3ee0a5ef44adf815045ae0c0dc5a42b8..ab9cce2e4e39ee9c0e84f019d9b460314c93f730 100644 --- a/compendium_v2/routes/user.py +++ b/compendium_v2/routes/user.py @@ -8,7 +8,7 @@ from sqlalchemy import select from compendium_v2.auth.session_management import admin_required from compendium_v2.db import db from compendium_v2.db.auth_model import User, ROLES -from compendium_v2.db.model import NREN +from compendium_v2.db.presentation_models import NREN from compendium_v2.routes import common routes = Blueprint('user', __name__) diff --git a/compendium_v2/static/survey-bundle.js b/compendium_v2/static/survey-bundle.js index 8ceee0afec939ee831ef0524272c6e0589dae5ea..e54038b0a7607b9025dcc23899d320460de39d0b 100644 --- a/compendium_v2/static/survey-bundle.js +++ b/compendium_v2/static/survey-bundle.js @@ -169,4 +169,4 @@ to { > * { pointer-events: auto; } -`,Pr=({reverseOrder:e,position:n="top-center",toastOptions:r,gutter:o,children:i,containerStyle:s,containerClassName:a})=>{let{toasts:l,handlers:u}=(e=>{let{toasts:n,pausedAt:r}=((e={})=>{let[n,r]=(0,t.useState)(Gn);(0,t.useEffect)((()=>($n.push(r),()=>{let e=$n.indexOf(r);e>-1&&$n.splice(e,1)})),[n]);let o=n.toasts.map((t=>{var n,r;return{...e,...e[t.type],...t,duration:t.duration||(null==(n=e[t.type])?void 0:n.duration)||(null==e?void 0:e.duration)||Zn[t.type],style:{...e.style,...null==(r=e[t.type])?void 0:r.style,...t.style}}}));return{...n,toasts:o}})(e);(0,t.useEffect)((()=>{if(r)return;let e=Date.now(),t=n.map((t=>{if(t.duration===1/0)return;let n=(t.duration||0)+t.pauseDuration-(e-t.createdAt);if(!(n<0))return setTimeout((()=>Xn.dismiss(t.id)),n);t.visible&&Xn.dismiss(t.id)}));return()=>{t.forEach((e=>e&&clearTimeout(e)))}}),[n,r]);let o=(0,t.useCallback)((()=>{r&&Kn({type:6,time:Date.now()})}),[r]),i=(0,t.useCallback)(((e,t)=>{let{reverseOrder:r=!1,gutter:o=8,defaultPosition:i}=t||{},s=n.filter((t=>(t.position||i)===(e.position||i)&&t.height)),a=s.findIndex((t=>t.id===e.id)),l=s.filter(((e,t)=>t<a&&e.visible)).length;return s.filter((e=>e.visible)).slice(...r?[l+1]:[0,l]).reduce(((e,t)=>e+(t.height||0)+o),0)}),[n]);return{toasts:n,handlers:{updateHeight:er,startPause:tr,endPause:o,calculateOffset:i}}})(r);return t.createElement("div",{style:{position:"fixed",zIndex:9999,top:16,left:16,right:16,bottom:16,pointerEvents:"none",...s},className:a,onMouseEnter:u.startPause,onMouseLeave:u.endPause},l.map((r=>{let s=r.position||n,a=((e,t)=>{let n=e.includes("top"),r=n?{top:0}:{bottom:0},o=e.includes("center")?{justifyContent:"center"}:e.includes("right")?{justifyContent:"flex-end"}:{};return{left:0,right:0,display:"flex",position:"absolute",transition:Hn()?void 0:"all 230ms cubic-bezier(.21,1.02,.73,1)",transform:`translateY(${t*(n?1:-1)}px)`,...r,...o}})(s,u.calculateOffset(r,{reverseOrder:e,gutter:o,defaultPosition:n}));return t.createElement(wr,{id:r.id,key:r.id,onHeightUpdate:u.updateHeight,className:r.visible?xr:"",style:a},"custom"===r.type?Qn(r.message,r):i?i(r):t.createElement(Cr,{toast:r,position:s}))})))},Sr=Xn,Vr=function(e){return e.Unverified="unverified",e.Verified="verified",e.Edited="edited",e}({}),Er=function(e){return e.not_started="not started",e.started="started",e.completed="completed",e}({}),Or=function(e){return e.closed="closed",e.open="open",e.preview="preview",e.published="published",e}({});function Tr(){return _r.apply(this,arguments)}function _r(){return(_r=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch("/api/survey/list");case 3:return t=e.sent,e.next=6,t.json();case 6:return n=e.sent,e.abrupt("return",n);case 10:return e.prev=10,e.t0=e.catch(0),console.log("failed fetching survey list.."),e.abrupt("return",[]);case 14:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}const Rr=t.forwardRef((({bsPrefix:e,variant:t,animation:n="border",size:r,as:o="div",className:i,...s},a)=>{const l=`${e=wt(e,"spinner")}-${n}`;return(0,yt.jsx)(o,{ref:a,...s,className:dt()(i,l,r&&`${l}-${r}`,t&&`text-${t}`)})}));Rr.displayName="Spinner";const Ir=Rr;function Dr(e){var n=e.text,r=e.helpText,o=e.onClick,i=e.enabled,s=lt((0,t.useState)(!1),2),a=s[0],l=s[1],u=function(){var e=it(ct().mark((function e(){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!a){e.next=2;break}return e.abrupt("return");case 2:return l(!0),e.prev=3,e.next=6,o();case 6:return e.prev=6,l(!1),e.finish(6);case 9:case"end":return e.stop()}}),e,null,[[3,,6,9]])})));return function(){return e.apply(this,arguments)}}();return t.createElement(Sn,{onClick:u,disabled:!i,style:{pointerEvents:"auto",marginLeft:".5rem"},title:r},a&&t.createElement(Ir,{as:"span",animation:"border",size:"sm",role:"status","aria-hidden":"true"}),n)}const jr=function(){var e=lt((0,t.useState)([]),2),n=e[0],r=e[1],o=(0,t.useRef)(!1);function i(e,t,n){return s.apply(this,arguments)}function s(){return(s=it(ct().mark((function e(t,n,o){var i,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch(t,{method:"POST"});case 3:return i=e.sent,e.next=6,i.json();case 6:s=e.sent,i.ok?(Sr(o),Tr().then((function(e){r(e)}))):Sr(n+s.message),e.next=13;break;case 10:e.prev=10,e.t0=e.catch(0),Sr(n+e.t0.message);case 13:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}function a(){return(a=it(ct().mark((function e(){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,i("/api/survey/new","Failed creating new survey: ","Created new survey");case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function l(e,t){return u.apply(this,arguments)}function u(){return(u=it(ct().mark((function e(t,n){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!o.current){e.next=3;break}return Sr("Wait for status update to be finished..."),e.abrupt("return");case 3:return o.current=!0,e.next=6,i("/api/survey/"+n+"/"+t,"Error while updating "+t+" survey status to "+n+": ",t+" survey status updated to "+n);case 6:o.current=!1;case 7:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function c(){return(c=it(ct().mark((function e(t,n){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,i("/api/response/unlock/"+t+"/"+n,"Error while unlocking "+n+" "+t+" survey response: ",n+" "+t+" survey response unlocked");case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}(0,t.useEffect)((function(){Tr().then((function(e){r(e)}))}),[]);var p=n.length>0&&n.every((function(e){return e.status==Or.published})),d=je(),h=window.location.origin+"/data?preview";return t.createElement("div",null,t.createElement(Pr,null),t.createElement(Sn,{onClick:function(){return a.apply(this,arguments)},disabled:!p,style:{pointerEvents:"auto"},title:"Create a new survey for the next year. Only possible if all current surveys are published."},"start new survey"),t.createElement(bn,null,n.map((function(e){return t.createElement(bn.Item,{eventKey:e.year.toString(),key:e.year},t.createElement(bn.Header,null,e.year," - ",e.status),t.createElement(bn.Body,null,t.createElement("div",{style:{marginLeft:".5rem"}},t.createElement(Sn,{style:{marginLeft:".5rem"},onClick:function(){return d("/survey/admin/inspect/".concat(e.year))},title:"Open the survey for inspection with all questions visible and any visibleIf logic added to the title."},"Inspect Survey"),t.createElement(Sn,{style:{marginLeft:".5rem"},onClick:function(){return d("/survey/admin/try/".concat(e.year))},title:"Open the survey exactly as the nrens will see it, but without any nren data."},"Try Survey"),t.createElement(Dr,{text:"Mark as open",helpText:"Allow the NRENs to respond to this survey. Only 1 survey may be open at a time, and (pre)-published surveys cannot be opened anymore.",enabled:e.status==Or.closed,onClick:function(){return l(e.year,"open")}}),t.createElement(Dr,{text:"Mark as closed",helpText:"Do not allow the NRENs to respond to this survey anymore. Only surveys with status open can be closed.",enabled:e.status==Or.open,onClick:function(){return l(e.year,"close")}}),t.createElement(Dr,{text:"Preview results",helpText:"Publish all completed survey responses to the compendium website for preview by admins. This is only possible if the survey is closed or previewed already.",enabled:e.status==Or.closed||e.status==Or.preview,onClick:function(){return l(e.year,"preview")}}),t.createElement(Dr,{text:"Publish results",helpText:"Publish or re-publish all completed survey responses to the compendium website. This is only possible if the survey is in preview or published already.",enabled:e.status==Or.preview||e.status==Or.published,onClick:function(){return l(e.year,"publish")}}),e.status==Or.preview&&t.createElement("span",null," Preview link: ",t.createElement("a",{href:h},h))),t.createElement(Vn,null,t.createElement("tbody",null,e.responses.map((function(n){return t.createElement("tr",{key:n.nren},t.createElement("td",null,n.nren),t.createElement("td",null,n.status),t.createElement("td",null,n.lock_description),t.createElement("td",null,t.createElement(Sn,{onClick:function(){return d("/survey/response/".concat(e.year,"/").concat(n.nren))},style:{pointerEvents:"auto"},title:"Open the responses of the NREN."},"open"),t.createElement(Sn,{onClick:function(){return function(e,t){return c.apply(this,arguments)}(e.year,n.nren)},disabled:""==n.lock_description,style:{pointerEvents:"auto"},title:"Remove the lock from the survey so that another person can open the survey for editing. WARNING: The person that currently has the lock will not be abe to save their changes anymore once someone else starts editing!"},"remove lock")))}))))))}))))};function kr(e){return kr="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},kr(e)}function Mr(e,t,n){return(t=function(e){var t=function(e,t){if("object"!==kr(e)||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,"string");if("object"!==kr(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"===kr(t)?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}const Lr=t.forwardRef((({bsPrefix:e,fluid:t=!1,as:n="div",className:r,...o},i)=>{const s=wt(e,"container"),a="string"==typeof t?`-${t}`:"-fluid";return(0,yt.jsx)(n,{ref:i,...o,className:dt()(r,t?`${s}${a}`:s)})}));Lr.displayName="Container";const qr=Lr,Nr=t.forwardRef((({bsPrefix:e,className:t,as:n="div",...r},o)=>{const i=wt(e,"row"),s=xt(),a=Pt(),l=`${i}-cols`,u=[];return s.forEach((e=>{const t=r[e];let n;delete r[e],null!=t&&"object"==typeof t?({cols:n}=t):n=t;const o=e!==a?`-${e}`:"";null!=n&&u.push(`${l}${o}-${n}`)})),(0,yt.jsx)(n,{ref:o,...r,className:dt()(t,i,...u)})}));Nr.displayName="Row";const Ar=Nr;function Br(){return(Br=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/user");case 2:return t=e.sent,e.next=5,t.json();case 5:return n=e.sent,e.abrupt("return",n);case 7:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var Fr={name:"",email:"",permissions:{admin:!1,active:!1},id:"",nrens:[],oidc_sub:"",role:""},Qr=(0,t.createContext)({user:Fr,logout:function(){}});const zr=function(e){var n=e.children,r=lt((0,t.useState)(Fr),2),o=r[0],i=r[1];function s(){return(s=it(ct().mark((function e(){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/logout");case 2:i(Fr);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}return(0,t.useEffect)((function(){(function(){return Br.apply(this,arguments)})().then((function(e){i(e)}))}),[]),t.createElement(Qr.Provider,{value:{user:o,logout:function(){return s.apply(this,arguments)}}},n)};function Hr(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Ur(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?Hr(Object(n),!0).forEach((function(t){Mr(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Hr(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function Wr(){return(Wr=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch("/api/user/list");case 3:return t=e.sent,e.next=6,t.json();case 6:return n=e.sent,e.abrupt("return",n);case 10:return e.prev=10,e.t0=e.catch(0),console.log("handle this better.."),e.abrupt("return",[]);case 14:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}function Jr(){return(Jr=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch("/api/nren/list");case 3:return t=e.sent,e.next=6,t.json();case 6:return n=e.sent,e.abrupt("return",n);case 10:return e.prev=10,e.t0=e.catch(0),console.log("handle this better.."),e.abrupt("return",[]);case 14:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}var $r=function(){var e=it(ct().mark((function e(t,n){var r,o,i,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=Ur({id:t},n),o={method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)},e.next=4,fetch("/api/user/".concat(t),o);case 4:return i=e.sent,e.next=7,i.json();case 7:if(s=e.sent,i.ok){e.next=10;break}throw new Error(s.message);case 10:return e.abrupt("return",s.user);case 11:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),Gr=function(e,t){return e.permissions.active&&!t.permissions.active?-1:!e.permissions.active&&t.permissions.active?1:e.permissions.active&&t.permissions.active?"admin"===e.role&&"admin"!==t.role?1:"admin"!==e.role&&"admin"===t.role?-1:e.name.localeCompare(t.name):e.name.localeCompare(t.name)};const Kr=function(){var e=lt((0,t.useState)([]),2),n=e[0],r=e[1],o=lt((0,t.useState)([]),2),i=o[0],s=o[1],a=(0,t.useContext)(Qr).user,l=lt((0,t.useState)({idx:-1,func:Gr,asc:!0}),2),u=l[0],c=l[1],p=lt((0,t.useState)([]),2),d=p[0],h=p[1];(0,t.useEffect)((function(){(function(){return Wr.apply(this,arguments)})().then((function(e){r(e),h(e.sort(u.func))})),function(){return Jr.apply(this,arguments)}().then((function(e){s(e.sort((function(e,t){return e.name.localeCompare(t.name)})))}))}),[]);for(var f=function(e,t){var o,i=n.findIndex((function(e){return e.id===t.id})),s=function(e){if(Array.isArray(e))return st(e)}(o=n)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(o)||at(o)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(),a=e.target.name,l={};l[a]="active"===a?e.target.checked:e.target.value,$r(t.id,l).then((function(e){s[i]=e,r(s)})).catch((function(e){alert(e.message)}))},g=function(e){if(e===u.idx||(5===e||0===e)&&-1===u.idx)return 5!==e&&0!==e||(e=-1),void c({idx:e,asc:!u.asc,func:function(e,t){return u.func(t,e)}});c(0===e?{idx:-1,asc:!0,func:Gr}:1===e?{idx:e,asc:!0,func:function(e,t){return e.permissions.active&&!t.permissions.active?-1:!e.permissions.active&&t.permissions.active?1:0}}:2===e?{idx:e,asc:!0,func:function(e,t){return e.role.localeCompare(t.role)}}:3===e?{idx:e,asc:!0,func:function(e,t){return e.email.localeCompare(t.email)}}:4===e?{idx:e,asc:!0,func:function(e,t){return e.name.localeCompare(t.name)}}:5===e?{idx:-1,asc:!0,func:Gr}:6===e?{idx:e,asc:!0,func:function(e,t){return 0===e.nrens.length&&0===t.nrens.length?0:0===e.nrens.length?-1:0===t.nrens.length?1:e.nrens[0].localeCompare(t.nrens[0])}}:{idx:e,asc:!0,func:Gr}),h(n.sort(u.func))},m={},y=0;y<=6;y++)m[y]=u.idx===y?{"aria-sort":u.asc?"ascending":"descending"}:null;return t.createElement(qr,{style:{maxWidth:"90vw"}},t.createElement(Ar,null,t.createElement("h1",null," User Management Page"),t.createElement(Vn,null,t.createElement("thead",null,t.createElement("tr",null,t.createElement("th",ht({},m[0],{onClick:function(){return g(0)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Id"),t.createElement("th",ht({},m[1],{onClick:function(){return g(1)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Active"),t.createElement("th",ht({},m[2],{onClick:function(){return g(2)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Role"),t.createElement("th",ht({},m[3],{onClick:function(){return g(3)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Email"),t.createElement("th",ht({},m[4],{onClick:function(){return g(4)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Full Name"),t.createElement("th",ht({},m[5],{onClick:function(){return g(5)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"OIDC Sub"),t.createElement("th",ht({},m[6],{onClick:function(){return g(6)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"NREN"))),t.createElement("tbody",null,d.map((function(e){return t.createElement("tr",{key:e.id},t.createElement("td",{style:{border:"1px dotted #ddd"}},e.id),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.id==a.id?"Active":t.createElement("input",{type:"checkbox",name:"active",checked:e.permissions.active,onChange:function(t){return f(t,e)}})),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.id==a.id?e.role.charAt(0).toUpperCase()+e.role.slice(1):t.createElement("select",{name:"role",defaultValue:e.role,onChange:function(t){return f(t,e)}},t.createElement("option",{value:"admin"},"Admin"),t.createElement("option",{value:"user"},"User"),t.createElement("option",{value:"observer"},"Observer"))),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.email),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.name),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.oidc_sub),t.createElement("td",{style:{border:"1px dotted #ddd"}},t.createElement("select",{name:"nren",multiple:!1,defaultValue:e.nrens.length>0?(n=e.nrens[0],null===(r=i.find((function(e){return e.id==n||e.name==n})))||void 0===r?void 0:r.id):void 0,onChange:function(t){return f(t,e)}},t.createElement("option",null,"Select NREN"),i.map((function(e){return t.createElement("option",{key:e.id,value:e.id},e.name)})))));var n,r}))))))};var Zr=n(535),Yr=n(352);function Xr(e,t){if(0==t.column.indexValue&&"item"in t.row){var n,r,o=t.row.item;void 0!==o.customDescription&&(null===(n=t.htmlElement.parentElement)||void 0===n||n.children[0].children[0].setAttribute("description",o.customDescription),null===(r=t.htmlElement.parentElement)||void 0===r||r.children[0].children[0].classList.add("survey-tooltip"))}}function eo(e){var t=e[0];if(void 0===t||null==t||""==t)return!0;try{var n=new URL(t);return"http:"===n.protocol||"https:"===n.protocol}catch(e){return!1}}function to(e,t){t.question.hideCheckboxLabels&&(t.cssClasses.root+=" hidden-checkbox-labels")}function no(e,t){var n,r='[data-name="'+t.question.name+'"]',o=null===(n=document.querySelector(r))||void 0===n?void 0:n.querySelector("h5");o&&!o.classList.contains("sv-header-flex")&&t.question.updateElementCss()}function ro(e,t,n){var r;n.verificationStatus.set(e.name,t);var o=document.createElement("button");o.type="button",o.className="sv-action-bar-item verification",o.innerHTML=t,t==Vr.Unverified?(o.innerHTML="No change from previous year",o.className+=" verification-required",o.onclick=function(){"display"!=n.mode&&(e.validate(),ro(e,Vr.Verified,n))}):(o.innerHTML="Answer updated",o.className+=" verification-ok");var i='[data-name="'+e.name+'"]',s=null===(r=document.querySelector(i))||void 0===r?void 0:r.querySelector("h5"),a=null==s?void 0:s.querySelector(".verification");a?a.replaceWith(o):null==s||s.appendChild(o)}const oo=function(e){var n=e.surveyModel,r=(0,t.useCallback)((function(e,t){var r=n.verificationStatus.get(t.question.name);r&&ro(t.question,r,n)}),[n]),o=(0,t.useCallback)((function(e,t){n.verificationStatus.get(t.question.name)==Vr.Unverified&&ro(t.question,Vr.Edited,n)}),[n]);return Zr.FunctionFactory.Instance.hasFunction("validateWebsiteUrl")||Zr.FunctionFactory.Instance.register("validateWebsiteUrl",eo),n.css.question.title.includes("sv-header-flex")||(n.css.question.title="sv-title sv-question__title sv-header-flex",n.css.question.titleOnError="sv-question__title--error sv-error-color-fix"),n.onAfterRenderQuestion.hasFunc(r)||(n.onAfterRenderQuestion.add(r),n.onAfterRenderQuestion.add(no)),n.onValueChanged.hasFunc(o)||n.onValueChanged.add(o),n.onUpdateQuestionCssClasses.hasFunc(to)||n.onUpdateQuestionCssClasses.add(to),n.onMatrixAfterCellRender.hasFunc(Xr)||n.onMatrixAfterCellRender.add(Xr),t.createElement(Yr.Survey,{model:n})},io=t.forwardRef(((e,t)=>{const[{className:n,...r},{as:o="div",bsPrefix:i,spans:s}]=function({as:e,bsPrefix:t,className:n,...r}){t=wt(t,"col");const o=xt(),i=Pt(),s=[],a=[];return o.forEach((e=>{const n=r[e];let o,l,u;delete r[e],"object"==typeof n&&null!=n?({span:o,offset:l,order:u}=n):o=n;const c=e!==i?`-${e}`:"";o&&s.push(!0===o?`${t}${c}`:`${t}${c}-${o}`),null!=u&&a.push(`order${c}-${u}`),null!=l&&a.push(`offset${c}-${l}`)})),[{...r,className:dt()(n,...s,...a)},{as:e,bsPrefix:t,spans:s}]}(e);return(0,yt.jsx)(o,{...r,ref:t,className:dt()(n,!s.length&&i)})}));io.displayName="Col";const so=io;function ao(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function lo(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?ao(Object(n),!0).forEach((function(t){Mr(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):ao(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}const uo=function(e){var n=e.surveyModel,r=e.pageNoSetter,o=lt((0,t.useState)([]),2),i=o[0],s=o[1],a=function(e){return!(null===e.value||void 0===e.value||""===e.value||"checkbox"===e.getType()&&0==e.value.length||"multipletext"===e.getType()&&(1===Object.keys(e.value).length&&void 0===Object.values(e.value)[0]||0===Object.keys(e.value).length))};(0,t.useEffect)((function(){var e=function(e){if(e&&e.pages){var t=[];e.pages.forEach((function(n){var r=n.questions.filter((function(e){return e.startWithNewLine})),o=r.length,i=r.filter(a).length,s=o-i,l=i/o;t.push({completionPercentage:100*l,unansweredPercentage:s/o*100,totalPages:e.pages.length,pageTitle:n.title})})),s(t)}};n.onValueChanged.add((function(t){e(t)})),e(n)}),[n]);var l={height:"0.5rem",transition:"width 0.3s ease"};return t.createElement(qr,{className:"survey-progress"},t.createElement(Ar,null,i.map((function(e,o){return t.createElement(so,{xs:12,md:!0,key:o,onClick:function(){return r(o)},style:{cursor:"pointer",margin:"0.5rem"}},t.createElement("div",null,t.createElement("span",{style:{whiteSpace:"nowrap",fontSize:"1.5rem",marginRight:"0.25rem",fontWeight:"bold",color:"#2db394"}},o+1),t.createElement("span",{style:lo({whiteSpace:"nowrap"},n.currentPageNo==o&&{fontWeight:"bold"})},e.pageTitle),t.createElement("div",{style:{display:"flex",flexWrap:"wrap"}},t.createElement("div",{style:lo(lo({},l),{},{width:"".concat(e.completionPercentage,"%"),backgroundColor:"#262261"})}),t.createElement("div",{style:lo(lo({},l),{},{width:"".concat(e.unansweredPercentage,"%"),backgroundColor:"#cdcdcd"})}))))}))))},co=function(e){var n=e.surveyModel,r=e.surveyActions,o=e.year,i=e.nren,s=e.children,a=lt((0,t.useState)(0),2),l=a[0],u=a[1],c=lt((0,t.useState)(!1),2),p=c[0],d=c[1],h=lt((0,t.useState)(""),2),f=h[0],g=h[1],m=lt((0,t.useState)(""),2),y=m[0],v=m[1],b=(0,t.useContext)(Qr).user,C=(0,t.useCallback)((function(){d("edit"==n.mode),g(n.lockedBy),u(n.currentPageNo),v(n.status)}),[n]);(0,t.useEffect)((function(){C()}),[C]);var w=function(e){u(e),n.currentPageNo=e},x=function(){w(n.currentPageNo+1)},P=function(){var e=it(ct().mark((function e(t){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,r[t]();case 2:C();case 3:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),S=function(e,t){return V(e,(function(){return P(t)}))},V=function(e,n){return t.createElement("button",{className:"sv-btn sv-btn--navigation",onClick:n},e)},E="Save and stop editing",O="Save progress",T="Start editing",_="Complete Survey",R=function(){return t.createElement("div",{className:"survey-edit-buttons-block"},!p&&!f&&n.editAllowed&&S(T,"startEdit"),!p&&f&&f==b.name&&S("Discard any unsaved changes and release your lock","releaseLock"),p&&y==Er.started&&S(O,"save"),p&&y==Er.started&&S(E,"saveAndStopEdit"),p&&l===n.visiblePages.length-1&&S(_,"complete"),l!==n.visiblePages.length-1&&V("Next Section",x))},I=parseInt(o);return t.createElement(qr,null,t.createElement(Ar,{className:"survey-content"},t.createElement("h2",null,t.createElement("span",{className:"survey-title"},o," Compendium Survey "),t.createElement("span",{className:"survey-title-nren"}," ",i," "),t.createElement("span",null," - ",y)),t.createElement("p",{style:{marginTop:"1rem"}},"To get started, click “",T,"” to end read-only mode. This is only possible when nobody else from your NREN is currently working on the survey.",t.createElement("br",null),"Where available, the survey questions are pre-filled with answers from the previous year. The survey asks about the past year, i.e. the ",o," survey asks about data from ",I-1," (or ",I-1,"/",o," if your NRENs financial year does not match the calendar year). You can edit the prefilled answer to provide new information, or press the “no change from previous year” button.",t.createElement("br",null),"Press the “",O,"“ or “",E,"“ button to save all answers in the survey. When you reach the last section of the survey (Services), you will find a “",_,"“ button which saves all answers in the survey and lets the Compendium team know that your answers are ready to be published.",t.createElement("br",null),"As long as the survey remains open, any Compendium administrator from your NREN can add answers or amend existing ones, even after using the “",_,"“ button. Different people from your NREN can contribute to the survey if needed.",t.createElement("br",null),"Some fields require specific data, such as numerical data, valid http-addresses, and in some questions, the answer has to add up to 100%. If an answer does not fulfil the set criteria, the question will turn pink and an error message will appear. Fields can be left blank if you prefer not to answer a question.",t.createElement("br",null),"If you notice any errors after the survey was closed, please contact us for correcting those."),t.createElement("p",null,"Thank you for taking the time to fill in the ",o," Compendium Survey. Any questions or requests can be sent to ",t.createElement("a",{href:"mailto:Partner-Relations@geant.org"},t.createElement("span",null,"Partner-Relations@geant.org"))),p&&t.createElement(t.Fragment,null,t.createElement("br",null),t.createElement("b",null,"Remember to click “",E,"” before leaving the page."))),t.createElement(Ar,null,R()),t.createElement(Ar,{className:"survey-content"},!p&&t.createElement("div",{className:"survey-edit-explainer"},!f&&n.editAllowed&&"The survey is in read-only mode; click the “Start editing“ button to begin editing the answers.",!f&&!n.editAllowed&&"The survey is in read-only mode and can not be edited by you.",f&&f!=b.name&&"The survey is in read-only mode and currently being edited by: "+f+". To start editing the survey, ask them to complete their edits.",f&&f==b.name&&'The survey is in read-only mode because you started editing in another tab, browser or device. To start editing the survey, either complete those edits or click the "Discard any unsaved changes" button.')),t.createElement(Ar,null,t.createElement(uo,{surveyModel:n,pageNoSetter:w}),s),t.createElement(Ar,null,R()))},po=function(e){var n=e.when,r=e.onPageExit;return function(e){let{router:n}=Qe(Be.UseBlocker),r=ze(Fe.UseBlocker),[o]=t.useState((()=>String(++Ue))),i=t.useCallback((t=>!!e()),[e]);n.getBlocker(o,i);t.useEffect((()=>()=>n.deleteBlocker(o)),[n,o]),r.blockers.get(o)}((function(){if(n()){var t=window.confirm(e.message);return t&&r(),!t}return!1})),t.createElement("div",null)};Zr.Serializer.addProperty("itemvalue","customDescription:text"),Zr.Serializer.addProperty("question","hideCheckboxLabels:boolean");const ho=function(e){var n=e.loadFrom,r=lt((0,t.useState)(),2),o=r[0],i=r[1],s=function(){let{matches:e}=t.useContext(Te),n=e[e.length-1];return n?n.params:{}}(),a=s.year,l=s.nren,u=lt((0,t.useState)("loading survey..."),2),c=u[0],p=u[1],d=(0,t.useCallback)((function(e){return e.preventDefault(),e.returnValue=""}),[]),h=(0,t.useCallback)((function(){window.navigator.sendBeacon("/api/response/unlock/"+a+"/"+l)}),[]),f=(0,t.useCallback)((function(){window.navigator.sendBeacon("/api/response/unlock/"+a+"/"+l),removeEventListener("beforeunload",d,{capture:!0}),removeEventListener("pagehide",h)}),[]);if((0,t.useEffect)((function(){function e(){return(e=it(ct().mark((function e(){var t,r,o,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch(n+a+(l?"/"+l:""));case 2:return t=e.sent,e.next=5,t.json();case 5:if(r=e.sent,t.ok){e.next=12;break}if(!("message"in r)){e.next=11;break}throw new Error(r.message);case 11:throw new Error("Request failed with status ".concat(t.status));case 12:for(s in(o=new Zr.Model(r.model)).setVariable("surveyyear",a),o.setVariable("previousyear",parseInt(a)-1),o.showNavigationButtons=!1,o.requiredText="",o.verificationStatus=new Map,r.verification_status)o.verificationStatus.set(s,r.verification_status[s]);o.data=r.data,o.clearIncorrectValues(!0),o.currentPageNo=r.page,o.mode=r.mode,o.lockedBy=r.locked_by,o.status=r.status,o.editAllowed=r.edit_allowed,i(o);case 27:case"end":return e.stop()}}),e)})))).apply(this,arguments)}(function(){return e.apply(this,arguments)})().catch((function(e){return p("Error when loading survey: "+e.message)}))}),[]),!o)return c;var g,m,y,v,b,C=function(){var e=it(ct().mark((function e(t,n){var r,i,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(l){e.next=2;break}return e.abrupt("return","Saving not available in inpect/try mode");case 2:return r={lock_uuid:t.lockUUID,new_state:n,data:t.data,page:t.currentPageNo,verification_status:Object.fromEntries(t.verificationStatus)},e.prev=3,e.next=6,fetch("/api/response/save/"+a+"/"+l,{method:"POST",headers:{"Content-Type":"application/json; charset=utf-8"},body:JSON.stringify(r)});case 6:return i=e.sent,e.next=9,i.json();case 9:if(s=e.sent,i.ok){e.next=12;break}return e.abrupt("return",s.message);case 12:o.mode=s.mode,o.lockedBy=s.locked_by,o.status=s.status,e.next=20;break;case 17:return e.prev=17,e.t0=e.catch(3),e.abrupt("return","Unknown Error: "+e.t0.message);case 20:case"end":return e.stop()}}),e,null,[[3,17]])})));return function(t,n){return e.apply(this,arguments)}}(),w=function(e){var t="",n=function(e,n){e.verificationStatus.get(n.name)==Vr.Unverified&&(""==t&&(t=n.name),n.error='Please verify that last years data is correct by editing the answer or pressing the "No change from previous year" button!')};o.onValidateQuestion.add(n);var r=e();return o.onValidateQuestion.remove(n),r||Sr("Validation failed!"),r},x={save:(b=it(ct().mark((function e(){var t;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,C(o,"editing");case 2:t=e.sent,Sr(t?"Failed saving survey: "+t:"Survey saved!");case 4:case"end":return e.stop()}}),e)}))),function(){return b.apply(this,arguments)}),complete:(v=it(ct().mark((function e(){var t;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!w(o.validate.bind(o,!0,!0))){e.next=6;break}return e.next=4,C(o,"completed");case 4:(t=e.sent)?Sr("Failed completing survey: "+t):(Sr("Survey completed!"),removeEventListener("beforeunload",d,{capture:!0}),removeEventListener("pagehide",h));case 6:case"end":return e.stop()}}),e)}))),function(){return v.apply(this,arguments)}),saveAndStopEdit:(y=it(ct().mark((function e(){var t;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,C(o,"readonly");case 2:(t=e.sent)?Sr("Failed saving survey: "+t):(Sr("Survey saved!"),removeEventListener("beforeunload",d,{capture:!0}),removeEventListener("pagehide",h));case 4:case"end":return e.stop()}}),e)}))),function(){return y.apply(this,arguments)}),startEdit:(m=it(ct().mark((function e(){var t,n,r;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/response/lock/"+a+"/"+l,{method:"POST"});case 2:return t=e.sent,e.next=5,t.json();case 5:if(n=e.sent,t.ok){e.next=9;break}return Sr("Failed starting edit: "+n.message),e.abrupt("return");case 9:for(r in addEventListener("pagehide",h),addEventListener("beforeunload",d,{capture:!0}),n.verification_status)o.verificationStatus.set(r,n.verification_status[r]);o.data=n.data,o.clearIncorrectValues(!0),o.mode=n.mode,o.lockedBy=n.locked_by,o.lockUUID=n.lock_uuid,o.status=n.status;case 18:case"end":return e.stop()}}),e)}))),function(){return m.apply(this,arguments)}),releaseLock:(g=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/response/unlock/"+a+"/"+l,{method:"POST"});case 2:return t=e.sent,e.next=5,t.json();case 5:if(n=e.sent,t.ok){e.next=9;break}return Sr("Failed releasing lock: "+n.message),e.abrupt("return");case 9:o.mode=n.mode,o.lockedBy=n.locked_by,o.status=n.status;case 12:case"end":return e.stop()}}),e)}))),function(){return g.apply(this,arguments)}),validatePage:function(){w(o.validatePage.bind(o))&&Sr("Page validation successful!")}};return t.createElement(qr,{className:"survey-container"},t.createElement(Pr,null),t.createElement(po,{message:"Are you sure you want to leave this page? Information you've entered may not be saved.",when:function(){return"edit"==o.mode&&!!l},onPageExit:f}),t.createElement(co,{surveyModel:o,surveyActions:x,year:a,nren:l},t.createElement(oo,{surveyModel:o})))},fo=n.p+"9ab20ac1d835b50b2e01.svg",go=function(){return t.createElement("div",{className:"external-page-nav-bar"},t.createElement(qr,null,t.createElement(Ar,null,t.createElement(so,{xs:10},t.createElement("div",{className:"nav-wrapper"},t.createElement("nav",{className:"header-nav"},t.createElement("a",{href:"https://geant.org/"},t.createElement("img",{src:fo})),t.createElement("ul",null,t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://network.geant.org/"},"NETWORK")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://geant.org/services/"},"SERVICES")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://community.geant.org/"},"COMMUNITY")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://tnc23.geant.org/"},"TNC")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://geant.org/projects/"},"PROJECTS")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://connect.geant.org/"},"CONNECT")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://impact.geant.org/"},"IMPACT")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://careers.geant.org/"},"CAREERS")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://about.geant.org/"},"ABOUT")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://connect.geant.org/community-news"},"NEWS")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://resources.geant.org/"},"RESOURCES")))))))))},mo=function(){var e,n=(0,t.useContext)(Qr).user,r=je(),o=!!n.id,i=!!o&&!!n.nrens.length,s=i?n.nrens[0]:"",a=!!o&&n.permissions.admin,l=!!o&&"observer"===n.role,u=function(){var e=lt((0,t.useState)(),2),n=e[0],r=e[1];return(0,t.useEffect)((function(){Tr().then((function(e){r(e[0])}))}),[]),t.createElement(Vn,{striped:!0,bordered:!0,responsive:!0},t.createElement("thead",null,t.createElement("tr",null,t.createElement("th",null,"(N)REN"),t.createElement("th",null,"Link"),t.createElement("th",null,"Survey Status"))),t.createElement("tbody",null,n&&n.responses.map((function(e){return t.createElement("tr",{key:e.nren},t.createElement("td",null,e.nren),t.createElement("td",null,t.createElement(tt,{to:"/survey/response/".concat(n.year,"/").concat(e.nren)},t.createElement("span",null,"Navigate to survey"))),t.createElement("td",null,e.status))}))))};return t.createElement(qr,{className:"py-5 grey-container"},t.createElement(Ar,null,t.createElement("div",{className:"center-text"},t.createElement("h1",{className:"geant-header"},"THE GÉANT COMPENDIUM OF NRENS SURVEY"),t.createElement("div",{className:"wordwrap pt-4",style:{maxWidth:"75rem"}},t.createElement("p",{style:{textAlign:"left"}},"Hello,",t.createElement("br",null),"Welcome to the GÉANT Compendium Survey. (N)REN Compendium administrators can login via Single Sign On (SSO) ",t.createElement("a",{href:"/login"},"here"),", which will complete their registration to fill in the 2023 Compendium survey. This will send a notification to the Compendium administration team and they will assign you to your (N)REN.",t.createElement("br",null),"Once this step has been completed, you will receive an email from the administration team. We aim to get back to you the same working day, but sometimes may take a little longer.",t.createElement("br",null),"If you are not sure whether you are a Compendium Administrator for your (N)REN, please contact your GÉANT Partner Relations relationship manager.",t.createElement("br",null),"Thank you."),t.createElement("span",null,"Current registration status:"),t.createElement("br",null),t.createElement("br",null),a?t.createElement("ul",null,t.createElement("li",null,t.createElement("span",null,"You are logged in as a Compendium Administrator")),t.createElement("li",null,t.createElement("span",null,"Click ",t.createElement(tt,{to:"/survey/admin/surveys"},"here")," to access the survey management page.")),t.createElement("li",null,t.createElement("span",null,"Click ",t.createElement(tt,{to:"/survey/admin/users"},"here")," to access the user management page."))):t.createElement("ul",null,!a&&!l&&i&&(e=(new Date).getFullYear(),r("/survey/response/".concat(e,"/").concat(s)),t.createElement(t.Fragment,null)),o?t.createElement("li",null,t.createElement("span",null,"You are logged in")):t.createElement("li",null,t.createElement("span",null,"You are not logged in")),o&&!l&&!i&&t.createElement("li",null,t.createElement("span",null,"Your access to the survey has not yet been approved")),o&&!l&&!i&&t.createElement("li",null,t.createElement("span",null,"Once you have been approved, you will immediately be directed to the relevant survey upon visiting this page")),o&&l&&t.createElement("li",null,t.createElement("span",null,"You have read-only access to the following surveys:"))),o&&l&&t.createElement(u,null)))))};var yo,vo=(yo=[{path:"survey/admin/surveys",element:t.createElement(jr,null)},{path:"survey/admin/users",element:t.createElement(Kr,null)},{path:"survey/admin/inspect/:year",element:t.createElement(ho,{loadFrom:"/api/response/inspect/"})},{path:"survey/admin/try/:year",element:t.createElement(ho,{loadFrom:"/api/response/try/"})},{path:"survey/response/:year/:nren",element:t.createElement(ho,{loadFrom:"/api/response/load/"})},{path:"*",element:t.createElement(mo,null)}],function(t){let n;if(s(t.routes.length>0,"You must provide a non-empty routes array to createRouter"),t.mapRouteProperties)n=t.mapRouteProperties;else if(t.detectErrorBoundary){let e=t.detectErrorBoundary;n=t=>({hasErrorBoundary:e(t)})}else n=Z;let r,i={},l=f(t.routes,n,void 0,i),c=t.basename||"/",p=o({v7_normalizeFormMethod:!1,v7_prependBasename:!1},t.future),h=null,m=new Set,y=null,v=null,b=null,C=null!=t.hydrationData,w=g(l,t.history.location,c),x=null;if(null==w){let e=pe(404,{pathname:t.history.location.pathname}),{matches:n,route:r}=ce(l);w=n,x={[r.id]:e}}let P,S,V=!(w.some((e=>e.route.lazy))||w.some((e=>e.route.loader))&&null==t.hydrationData),E={historyAction:t.history.action,location:t.history.location,matches:w,initialized:V,navigation:U,restoreScrollPosition:null==t.hydrationData&&null,preventScrollReset:!1,revalidation:"idle",loaderData:t.hydrationData&&t.hydrationData.loaderData||{},actionData:t.hydrationData&&t.hydrationData.actionData||null,errors:t.hydrationData&&t.hydrationData.errors||x,fetchers:new Map,blockers:new Map},O=e.Pop,T=!1,R=!1,I=!1,D=[],j=[],k=new Map,M=0,L=-1,q=new Map,N=new Set,A=new Map,B=new Map,F=new Map,Q=!1;function z(e){E=o({},E,e),m.forEach((e=>e(E)))}function te(n,i){var s,a;let u,c=null!=E.actionData&&null!=E.navigation.formMethod&&ye(E.navigation.formMethod)&&"loading"===E.navigation.state&&!0!==(null==(s=n.state)?void 0:s._isRedirect);u=i.actionData?Object.keys(i.actionData).length>0?i.actionData:null:c?E.actionData:null;let p=i.loaderData?le(E.loaderData,i.loaderData,i.matches||[],i.errors):E.loaderData;for(let[e]of F)_e(e);let d=!0===T||null!=E.navigation.formMethod&&ye(E.navigation.formMethod)&&!0!==(null==(a=n.state)?void 0:a._isRedirect);r&&(l=r,r=void 0),z(o({},i,{actionData:u,loaderData:p,historyAction:O,location:n,initialized:!0,navigation:U,revalidation:"idle",restoreScrollPosition:je(n,i.matches||E.matches),preventScrollReset:d,blockers:new Map(E.blockers)})),R||O===e.Pop||(O===e.Push?t.history.push(n,n.state):O===e.Replace&&t.history.replace(n,n.state)),O=e.Pop,T=!1,R=!1,I=!1,D=[],j=[]}async function ne(s,a,u){S&&S.abort(),S=null,O=s,R=!0===(u&&u.startUninterruptedRevalidation),function(e,t){if(y&&v&&b){let n=t.map((e=>we(e,E.loaderData))),r=v(e,n)||e.key;y[r]=b()}}(E.location,E.matches),T=!0===(u&&u.preventScrollReset);let p=r||l,h=u&&u.overrideNavigation,f=g(p,a,c);if(!f){let e=pe(404,{pathname:a.pathname}),{matches:t,route:n}=ce(p);return De(),void te(a,{matches:t,loaderData:{},errors:{[n.id]:e}})}if(E.initialized&&function(e,t){return e.pathname===t.pathname&&e.search===t.search&&(""===e.hash?""!==t.hash:e.hash===t.hash||""!==t.hash)}(E.location,a)&&!(u&&u.submission&&ye(u.submission.formMethod)))return void te(a,{matches:f});S=new AbortController;let m,C,w=ie(t.history,a,S.signal,u&&u.submission);if(u&&u.pendingError)C={[ue(f).route.id]:u.pendingError};else if(u&&u.submission&&ye(u.submission.formMethod)){let t=await async function(t,r,s,a,l){let u;Ce(),z({navigation:o({state:"submitting",location:r},s)});let p=xe(a,r);if(p.route.action||p.route.lazy){if(u=await oe("action",t,p,a,i,n,c),t.signal.aborted)return{shortCircuited:!0}}else u={type:d.error,error:pe(405,{method:t.method,pathname:r.pathname,routeId:p.route.id})};if(me(u)){let e;return e=l&&null!=l.replace?l.replace:u.location===E.location.pathname+E.location.search,await se(E,u,{submission:s,replace:e}),{shortCircuited:!0}}if(ge(u)){let t=ue(a,p.route.id);return!0!==(l&&l.replace)&&(O=e.Push),{pendingActionData:{},pendingActionError:{[t.route.id]:u.error}}}if(fe(u))throw pe(400,{type:"defer-action"});return{pendingActionData:{[p.route.id]:u.data}}}(w,a,u.submission,f,{replace:u.replace});if(t.shortCircuited)return;m=t.pendingActionData,C=t.pendingActionError,h=o({state:"loading",location:a},u.submission),w=new Request(w.url,{signal:w.signal})}let{shortCircuited:x,loaderData:P,errors:V}=await async function(e,n,i,s,a,u,p,d,h){let f=s;f||(f=o({state:"loading",location:n,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0},a));let g=a||u?a||u:f.formMethod&&f.formAction&&f.formData&&f.formEncType?{formMethod:f.formMethod,formAction:f.formAction,formData:f.formData,formEncType:f.formEncType}:void 0,m=r||l,[y,v]=ee(t.history,E,i,g,n,I,D,j,A,m,c,d,h);if(De((e=>!(i&&i.some((t=>t.route.id===e)))||y&&y.some((t=>t.route.id===e)))),0===y.length&&0===v.length){let e=Oe();return te(n,o({matches:i,loaderData:{},errors:h||null},d?{actionData:d}:{},e?{fetchers:new Map(E.fetchers)}:{})),{shortCircuited:!0}}if(!R){v.forEach((e=>{let t=E.fetchers.get(e.key),n={state:"loading",data:t&&t.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(e.key,n)}));let e=d||E.actionData;z(o({navigation:f},e?0===Object.keys(e).length?{actionData:null}:{actionData:e}:{},v.length>0?{fetchers:new Map(E.fetchers)}:{}))}L=++M,v.forEach((e=>{e.controller&&k.set(e.key,e.controller)}));let b=()=>v.forEach((e=>Ve(e.key)));S&&S.signal.addEventListener("abort",b);let{results:C,loaderResults:w,fetcherResults:x}=await he(E.matches,i,y,v,e);if(e.signal.aborted)return{shortCircuited:!0};S&&S.signal.removeEventListener("abort",b),v.forEach((e=>k.delete(e.key)));let P=de(C);if(P)return await se(E,P,{replace:p}),{shortCircuited:!0};let{loaderData:V,errors:O}=ae(E,i,y,w,h,v,x,B);B.forEach(((e,t)=>{e.subscribe((n=>{(n||e.done)&&B.delete(t)}))}));let T=Oe(),_=Te(L);return o({loaderData:V,errors:O},T||_||v.length>0?{fetchers:new Map(E.fetchers)}:{})}(w,a,f,h,u&&u.submission,u&&u.fetcherSubmission,u&&u.replace,m,C);x||(S=null,te(a,o({matches:f},m?{actionData:m}:{},{loaderData:P,errors:V})))}function re(e){return E.fetchers.get(e)||W}async function se(n,r,i){var a;let{submission:l,replace:p,isFetchActionRedirect:d}=void 0===i?{}:i;r.revalidate&&(I=!0);let h=u(n.location,r.location,o({_isRedirect:!0},d?{_isFetchActionRedirect:!0}:{}));if(s(h,"Expected a location on the redirect navigation"),$.test(r.location)&&G&&void 0!==(null==(a=window)?void 0:a.location)){let e=t.history.createURL(r.location),n=null==_(e.pathname,c);if(window.location.origin!==e.origin||n)return void(p?window.location.replace(r.location):window.location.assign(r.location))}S=null;let f=!0===p?e.Replace:e.Push,{formMethod:g,formAction:m,formEncType:y,formData:v}=n.navigation;!l&&g&&m&&v&&y&&(l={formMethod:g,formAction:m,formEncType:y,formData:v}),H.has(r.status)&&l&&ye(l.formMethod)?await ne(f,h,{submission:o({},l,{formAction:r.location}),preventScrollReset:T}):d?await ne(f,h,{overrideNavigation:{state:"loading",location:h,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0},fetcherSubmission:l,preventScrollReset:T}):await ne(f,h,{overrideNavigation:{state:"loading",location:h,formMethod:l?l.formMethod:void 0,formAction:l?l.formAction:void 0,formEncType:l?l.formEncType:void 0,formData:l?l.formData:void 0},preventScrollReset:T})}async function he(e,r,o,s,a){let l=await Promise.all([...o.map((e=>oe("loader",a,e,r,i,n,c))),...s.map((e=>e.matches&&e.match&&e.controller?oe("loader",ie(t.history,e.path,e.controller.signal),e.match,e.matches,i,n,c):{type:d.error,error:pe(404,{pathname:e.path})}))]),u=l.slice(0,o.length),p=l.slice(o.length);return await Promise.all([ve(e,o,u,u.map((()=>a.signal)),!1,E.loaderData),ve(e,s.map((e=>e.match)),p,s.map((e=>e.controller?e.controller.signal:null)),!0)]),{results:l,loaderResults:u,fetcherResults:p}}function Ce(){I=!0,D.push(...De()),A.forEach(((e,t)=>{k.has(t)&&(j.push(t),Ve(t))}))}function Pe(e,t,n){let r=ue(E.matches,t);Se(e),z({errors:{[r.route.id]:n},fetchers:new Map(E.fetchers)})}function Se(e){k.has(e)&&Ve(e),A.delete(e),q.delete(e),N.delete(e),E.fetchers.delete(e)}function Ve(e){let t=k.get(e);s(t,"Expected fetch controller: "+e),t.abort(),k.delete(e)}function Ee(e){for(let t of e){let e={state:"idle",data:re(t).data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(t,e)}}function Oe(){let e=[],t=!1;for(let n of N){let r=E.fetchers.get(n);s(r,"Expected fetcher: "+n),"loading"===r.state&&(N.delete(n),e.push(n),t=!0)}return Ee(e),t}function Te(e){let t=[];for(let[n,r]of q)if(r<e){let e=E.fetchers.get(n);s(e,"Expected fetcher: "+n),"loading"===e.state&&(Ve(n),q.delete(n),t.push(n))}return Ee(t),t.length>0}function _e(e){E.blockers.delete(e),F.delete(e)}function Re(e,t){let n=E.blockers.get(e)||J;s("unblocked"===n.state&&"blocked"===t.state||"blocked"===n.state&&"blocked"===t.state||"blocked"===n.state&&"proceeding"===t.state||"blocked"===n.state&&"unblocked"===t.state||"proceeding"===n.state&&"unblocked"===t.state,"Invalid blocker state transition: "+n.state+" -> "+t.state),E.blockers.set(e,t),z({blockers:new Map(E.blockers)})}function Ie(e){let{currentLocation:t,nextLocation:n,historyAction:r}=e;if(0===F.size)return;F.size>1&&a(!1,"A router only supports one blocker at a time");let o=Array.from(F.entries()),[i,s]=o[o.length-1],l=E.blockers.get(i);return l&&"proceeding"===l.state?void 0:s({currentLocation:t,nextLocation:n,historyAction:r})?i:void 0}function De(e){let t=[];return B.forEach(((n,r)=>{e&&!e(r)||(n.cancel(),t.push(r),B.delete(r))})),t}function je(e,t){if(y&&v&&b){let n=t.map((e=>we(e,E.loaderData))),r=v(e,n)||e.key,o=y[r];if("number"==typeof o)return o}return null}return P={get basename(){return c},get state(){return E},get routes(){return l},initialize:function(){return h=t.history.listen((e=>{let{action:n,location:r,delta:o}=e;if(Q)return void(Q=!1);a(0===F.size||null!=o,"You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL.");let i=Ie({currentLocation:E.location,nextLocation:r,historyAction:n});return i&&null!=o?(Q=!0,t.history.go(-1*o),void Re(i,{state:"blocked",location:r,proceed(){Re(i,{state:"proceeding",proceed:void 0,reset:void 0,location:r}),t.history.go(o)},reset(){_e(i),z({blockers:new Map(P.state.blockers)})}})):ne(n,r)})),E.initialized||ne(e.Pop,E.location),P},subscribe:function(e){return m.add(e),()=>m.delete(e)},enableScrollRestoration:function(e,t,n){if(y=e,b=t,v=n||(e=>e.key),!C&&E.navigation===U){C=!0;let e=je(E.location,E.matches);null!=e&&z({restoreScrollPosition:e})}return()=>{y=null,b=null,v=null}},navigate:async function n(r,i){if("number"==typeof r)return void t.history.go(r);let s=Y(E.location,E.matches,c,p.v7_prependBasename,r,null==i?void 0:i.fromRouteId,null==i?void 0:i.relative),{path:a,submission:l,error:d}=X(p.v7_normalizeFormMethod,!1,s,i),h=E.location,f=u(E.location,a,i&&i.state);f=o({},f,t.history.encodeLocation(f));let g=i&&null!=i.replace?i.replace:void 0,m=e.Push;!0===g?m=e.Replace:!1===g||null!=l&&ye(l.formMethod)&&l.formAction===E.location.pathname+E.location.search&&(m=e.Replace);let y=i&&"preventScrollReset"in i?!0===i.preventScrollReset:void 0,v=Ie({currentLocation:h,nextLocation:f,historyAction:m});if(!v)return await ne(m,f,{submission:l,pendingError:d,preventScrollReset:y,replace:i&&i.replace});Re(v,{state:"blocked",location:f,proceed(){Re(v,{state:"proceeding",proceed:void 0,reset:void 0,location:f}),n(r,i)},reset(){_e(v),z({blockers:new Map(E.blockers)})}})},fetch:function(e,a,u,d){if(K)throw new Error("router.fetch() was called during the server render, but it shouldn't be. You are likely calling a useFetcher() method in the body of your component. Try moving it to a useEffect or a callback.");k.has(e)&&Ve(e);let h=r||l,f=Y(E.location,E.matches,c,p.v7_prependBasename,u,a,null==d?void 0:d.relative),m=g(h,f,c);if(!m)return void Pe(e,a,pe(404,{pathname:f}));let{path:y,submission:v}=X(p.v7_normalizeFormMethod,!0,f,d),b=xe(m,y);T=!0===(d&&d.preventScrollReset),v&&ye(v.formMethod)?async function(e,a,u,p,d,h){if(Ce(),A.delete(e),!p.route.action&&!p.route.lazy){let t=pe(405,{method:h.formMethod,pathname:u,routeId:a});return void Pe(e,a,t)}let f=E.fetchers.get(e),m=o({state:"submitting"},h,{data:f&&f.data," _hasFetcherDoneAnything ":!0});E.fetchers.set(e,m),z({fetchers:new Map(E.fetchers)});let y=new AbortController,v=ie(t.history,u,y.signal,h);k.set(e,y);let b=await oe("action",v,p,d,i,n,c);if(v.signal.aborted)return void(k.get(e)===y&&k.delete(e));if(me(b)){k.delete(e),N.add(e);let t=o({state:"loading"},h,{data:void 0," _hasFetcherDoneAnything ":!0});return E.fetchers.set(e,t),z({fetchers:new Map(E.fetchers)}),se(E,b,{submission:h,isFetchActionRedirect:!0})}if(ge(b))return void Pe(e,a,b.error);if(fe(b))throw pe(400,{type:"defer-action"});let C=E.navigation.location||E.location,w=ie(t.history,C,y.signal),x=r||l,P="idle"!==E.navigation.state?g(x,E.navigation.location,c):E.matches;s(P,"Didn't find any matches after fetcher action");let V=++M;q.set(e,V);let T=o({state:"loading",data:b.data},h,{" _hasFetcherDoneAnything ":!0});E.fetchers.set(e,T);let[_,R]=ee(t.history,E,P,h,C,I,D,j,A,x,c,{[p.route.id]:b.data},void 0);R.filter((t=>t.key!==e)).forEach((e=>{let t=e.key,n=E.fetchers.get(t),r={state:"loading",data:n&&n.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(t,r),e.controller&&k.set(t,e.controller)})),z({fetchers:new Map(E.fetchers)});let F=()=>R.forEach((e=>Ve(e.key)));y.signal.addEventListener("abort",F);let{results:Q,loaderResults:H,fetcherResults:U}=await he(E.matches,P,_,R,w);if(y.signal.aborted)return;y.signal.removeEventListener("abort",F),q.delete(e),k.delete(e),R.forEach((e=>k.delete(e.key)));let W=de(Q);if(W)return se(E,W);let{loaderData:J,errors:$}=ae(E,E.matches,_,H,void 0,R,U,B),G={state:"idle",data:b.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(e,G);let K=Te(V);"loading"===E.navigation.state&&V>L?(s(O,"Expected pending action"),S&&S.abort(),te(E.navigation.location,{matches:P,loaderData:J,errors:$,fetchers:new Map(E.fetchers)})):(z(o({errors:$,loaderData:le(E.loaderData,J,P,$)},K?{fetchers:new Map(E.fetchers)}:{})),I=!1)}(e,a,y,b,m,v):(A.set(e,{routeId:a,path:y}),async function(e,r,a,l,u,p){let d=E.fetchers.get(e),h=o({state:"loading",formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0},p,{data:d&&d.data," _hasFetcherDoneAnything ":!0});E.fetchers.set(e,h),z({fetchers:new Map(E.fetchers)});let f=new AbortController,g=ie(t.history,a,f.signal);k.set(e,f);let m=await oe("loader",g,l,u,i,n,c);if(fe(m)&&(m=await be(m,g.signal,!0)||m),k.get(e)===f&&k.delete(e),g.signal.aborted)return;if(me(m))return N.add(e),void await se(E,m);if(ge(m)){let t=ue(E.matches,r);return E.fetchers.delete(e),void z({fetchers:new Map(E.fetchers),errors:{[t.route.id]:m.error}})}s(!fe(m),"Unhandled fetcher deferred data");let y={state:"idle",data:m.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(e,y),z({fetchers:new Map(E.fetchers)})}(e,a,y,b,m,v))},revalidate:function(){Ce(),z({revalidation:"loading"}),"submitting"!==E.navigation.state&&("idle"!==E.navigation.state?ne(O||E.historyAction,E.navigation.location,{overrideNavigation:E.navigation}):ne(E.historyAction,E.location,{startUninterruptedRevalidation:!0}))},createHref:e=>t.history.createHref(e),encodeLocation:e=>t.history.encodeLocation(e),getFetcher:re,deleteFetcher:Se,dispose:function(){h&&h(),m.clear(),S&&S.abort(),E.fetchers.forEach(((e,t)=>Se(t))),E.blockers.forEach(((e,t)=>_e(t)))},getBlocker:function(e,t){let n=E.blockers.get(e)||J;return F.get(e)!==t&&F.set(e,t),n},deleteBlocker:_e,_internalFetchControllers:k,_internalActiveDeferreds:B,_internalSetRoutes:function(e){i={},r=f(e,n,void 0,i)}},P}({basename:void 0,future:Ke({},void 0,{v7_prependBasename:!0}),history:function(t){return void 0===t&&(t={}),function(t,n,r,a){void 0===a&&(a={});let{window:p=document.defaultView,v5Compat:d=!1}=a,h=p.history,f=e.Pop,g=null,m=y();function y(){return(h.state||{idx:null}).idx}function v(){f=e.Pop;let t=y(),n=null==t?null:t-m;m=t,g&&g({action:f,location:C.location,delta:n})}function b(e){let t="null"!==p.location.origin?p.location.origin:p.location.href,n="string"==typeof e?e:c(e);return s(t,"No window.location.(origin|href) available to create URL for href: "+n),new URL(n,t)}null==m&&(m=0,h.replaceState(o({},h.state,{idx:m}),""));let C={get action(){return f},get location(){return t(p,h)},listen(e){if(g)throw new Error("A history only accepts one active listener");return p.addEventListener(i,v),g=e,()=>{p.removeEventListener(i,v),g=null}},createHref:e=>n(p,e),createURL:b,encodeLocation(e){let t=b(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:function(t,n){f=e.Push;let o=u(C.location,t,n);r&&r(o,t),m=y()+1;let i=l(o,m),s=C.createHref(o);try{h.pushState(i,"",s)}catch(e){p.location.assign(s)}d&&g&&g({action:f,location:C.location,delta:1})},replace:function(t,n){f=e.Replace;let o=u(C.location,t,n);r&&r(o,t),m=y();let i=l(o,m),s=C.createHref(o);h.replaceState(i,"",s),d&&g&&g({action:f,location:C.location,delta:0})},go:e=>h.go(e)};return C}((function(e,t){let{pathname:n,search:r,hash:o}=e.location;return u("",{pathname:n,search:r,hash:o},t.state&&t.state.usr||null,t.state&&t.state.key||"default")}),(function(e,t){return"string"==typeof t?t:c(t)}),null,t)}({window:void 0}),hydrationData:function(){var e;let t=null==(e=window)?void 0:e.__staticRouterHydrationData;return t&&t.errors&&(t=Ke({},t,{errors:Ye(t.errors)})),t}(),routes:yo,mapRouteProperties:function(e){let n={hasErrorBoundary:null!=e.ErrorBoundary||null!=e.errorElement};return e.Component&&Object.assign(n,{element:t.createElement(e.Component),Component:void 0}),e.ErrorBoundary&&Object.assign(n,{errorElement:t.createElement(e.ErrorBoundary),ErrorBoundary:void 0}),n}}).initialize());const bo=function(){return t.createElement("div",{className:"app"},t.createElement(zr,null,t.createElement(go,null),t.createElement(We,{router:vo})))};var Co=document.getElementById("root");(0,r.s)(Co).render(t.createElement(t.StrictMode,null,t.createElement(bo,null)))})()})(); \ No newline at end of file +`,Pr=({reverseOrder:e,position:n="top-center",toastOptions:r,gutter:o,children:i,containerStyle:s,containerClassName:a})=>{let{toasts:l,handlers:u}=(e=>{let{toasts:n,pausedAt:r}=((e={})=>{let[n,r]=(0,t.useState)(Gn);(0,t.useEffect)((()=>($n.push(r),()=>{let e=$n.indexOf(r);e>-1&&$n.splice(e,1)})),[n]);let o=n.toasts.map((t=>{var n,r;return{...e,...e[t.type],...t,duration:t.duration||(null==(n=e[t.type])?void 0:n.duration)||(null==e?void 0:e.duration)||Zn[t.type],style:{...e.style,...null==(r=e[t.type])?void 0:r.style,...t.style}}}));return{...n,toasts:o}})(e);(0,t.useEffect)((()=>{if(r)return;let e=Date.now(),t=n.map((t=>{if(t.duration===1/0)return;let n=(t.duration||0)+t.pauseDuration-(e-t.createdAt);if(!(n<0))return setTimeout((()=>Xn.dismiss(t.id)),n);t.visible&&Xn.dismiss(t.id)}));return()=>{t.forEach((e=>e&&clearTimeout(e)))}}),[n,r]);let o=(0,t.useCallback)((()=>{r&&Kn({type:6,time:Date.now()})}),[r]),i=(0,t.useCallback)(((e,t)=>{let{reverseOrder:r=!1,gutter:o=8,defaultPosition:i}=t||{},s=n.filter((t=>(t.position||i)===(e.position||i)&&t.height)),a=s.findIndex((t=>t.id===e.id)),l=s.filter(((e,t)=>t<a&&e.visible)).length;return s.filter((e=>e.visible)).slice(...r?[l+1]:[0,l]).reduce(((e,t)=>e+(t.height||0)+o),0)}),[n]);return{toasts:n,handlers:{updateHeight:er,startPause:tr,endPause:o,calculateOffset:i}}})(r);return t.createElement("div",{style:{position:"fixed",zIndex:9999,top:16,left:16,right:16,bottom:16,pointerEvents:"none",...s},className:a,onMouseEnter:u.startPause,onMouseLeave:u.endPause},l.map((r=>{let s=r.position||n,a=((e,t)=>{let n=e.includes("top"),r=n?{top:0}:{bottom:0},o=e.includes("center")?{justifyContent:"center"}:e.includes("right")?{justifyContent:"flex-end"}:{};return{left:0,right:0,display:"flex",position:"absolute",transition:Hn()?void 0:"all 230ms cubic-bezier(.21,1.02,.73,1)",transform:`translateY(${t*(n?1:-1)}px)`,...r,...o}})(s,u.calculateOffset(r,{reverseOrder:e,gutter:o,defaultPosition:n}));return t.createElement(wr,{id:r.id,key:r.id,onHeightUpdate:u.updateHeight,className:r.visible?xr:"",style:a},"custom"===r.type?Qn(r.message,r):i?i(r):t.createElement(Cr,{toast:r,position:s}))})))},Sr=Xn,Vr=function(e){return e.Unverified="unverified",e.Verified="verified",e.Edited="edited",e}({}),Er=function(e){return e.not_started="not started",e.started="started",e.completed="completed",e}({}),Or=function(e){return e.closed="closed",e.open="open",e.preview="preview",e.published="published",e}({});function Tr(){return _r.apply(this,arguments)}function _r(){return(_r=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch("/api/survey/list");case 3:return t=e.sent,e.next=6,t.json();case 6:return n=e.sent,e.abrupt("return",n);case 10:return e.prev=10,e.t0=e.catch(0),console.log("failed fetching survey list.."),e.abrupt("return",[]);case 14:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}const Rr=t.forwardRef((({bsPrefix:e,variant:t,animation:n="border",size:r,as:o="div",className:i,...s},a)=>{const l=`${e=wt(e,"spinner")}-${n}`;return(0,yt.jsx)(o,{ref:a,...s,className:dt()(i,l,r&&`${l}-${r}`,t&&`text-${t}`)})}));Rr.displayName="Spinner";const Ir=Rr;function Dr(e){var n=e.text,r=e.helpText,o=e.onClick,i=e.enabled,s=lt((0,t.useState)(!1),2),a=s[0],l=s[1],u=function(){var e=it(ct().mark((function e(){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!a){e.next=2;break}return e.abrupt("return");case 2:return l(!0),e.prev=3,e.next=6,o();case 6:return e.prev=6,l(!1),e.finish(6);case 9:case"end":return e.stop()}}),e,null,[[3,,6,9]])})));return function(){return e.apply(this,arguments)}}();return t.createElement(Sn,{onClick:u,disabled:!i,style:{pointerEvents:"auto",marginLeft:".5rem"},title:r},a&&t.createElement(Ir,{as:"span",animation:"border",size:"sm",role:"status","aria-hidden":"true"}),n)}const jr=function(){var e=lt((0,t.useState)([]),2),n=e[0],r=e[1],o=(0,t.useRef)(!1);function i(e,t,n){return s.apply(this,arguments)}function s(){return(s=it(ct().mark((function e(t,n,o){var i,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch(t,{method:"POST"});case 3:return i=e.sent,e.next=6,i.json();case 6:s=e.sent,i.ok?(Sr(o),Tr().then((function(e){r(e)}))):Sr(n+s.message),e.next=13;break;case 10:e.prev=10,e.t0=e.catch(0),Sr(n+e.t0.message);case 13:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}function a(){return(a=it(ct().mark((function e(){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,i("/api/survey/new","Failed creating new survey: ","Created new survey");case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function l(e,t){return u.apply(this,arguments)}function u(){return(u=it(ct().mark((function e(t,n){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!o.current){e.next=3;break}return Sr("Wait for status update to be finished..."),e.abrupt("return");case 3:return o.current=!0,e.next=6,i("/api/survey/"+n+"/"+t,"Error while updating "+t+" survey status to "+n+": ",t+" survey status updated to "+n);case 6:o.current=!1;case 7:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function c(){return(c=it(ct().mark((function e(t,n){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,i("/api/response/unlock/"+t+"/"+n,"Error while unlocking "+n+" "+t+" survey response: ",n+" "+t+" survey response unlocked");case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}(0,t.useEffect)((function(){Tr().then((function(e){r(e)}))}),[]);var p=n.length>0&&n.every((function(e){return e.status==Or.published})),d=je(),h=window.location.origin+"/data?preview";return t.createElement("div",null,t.createElement(Pr,null),t.createElement(Sn,{onClick:function(){return a.apply(this,arguments)},disabled:!p,style:{pointerEvents:"auto"},title:"Create a new survey for the next year. Only possible if all current surveys are published."},"start new survey"),t.createElement(bn,null,n.map((function(e){return t.createElement(bn.Item,{eventKey:e.year.toString(),key:e.year},t.createElement(bn.Header,null,e.year," - ",e.status),t.createElement(bn.Body,null,t.createElement("div",{style:{marginLeft:".5rem"}},t.createElement(Sn,{style:{marginLeft:".5rem"},onClick:function(){return d("/survey/admin/inspect/".concat(e.year))},title:"Open the survey for inspection with all questions visible and any visibleIf logic added to the title."},"Inspect Survey"),t.createElement(Sn,{style:{marginLeft:".5rem"},onClick:function(){return d("/survey/admin/try/".concat(e.year))},title:"Open the survey exactly as the nrens will see it, but without any nren data."},"Try Survey"),t.createElement(Dr,{text:"Mark as open",helpText:"Allow the NRENs to respond to this survey. Only 1 survey may be open at a time, and (pre)-published surveys cannot be opened anymore.",enabled:e.status==Or.closed,onClick:function(){return l(e.year,"open")}}),t.createElement(Dr,{text:"Mark as closed",helpText:"Do not allow the NRENs to respond to this survey anymore. Only surveys with status open can be closed.",enabled:e.status==Or.open,onClick:function(){return l(e.year,"close")}}),t.createElement(Dr,{text:"Preview results",helpText:"Publish all completed survey responses to the compendium website for preview by admins. This is only possible if the survey is closed or previewed already.",enabled:e.status==Or.closed||e.status==Or.preview,onClick:function(){return l(e.year,"preview")}}),t.createElement(Dr,{text:"Publish results",helpText:"Publish or re-publish all completed survey responses to the compendium website. This is only possible if the survey is in preview or published already.",enabled:e.status==Or.preview||e.status==Or.published,onClick:function(){return l(e.year,"publish")}}),e.status==Or.preview&&t.createElement("span",null," Preview link: ",t.createElement("a",{href:h},h))),t.createElement(Vn,null,t.createElement("tbody",null,e.responses.map((function(n){return t.createElement("tr",{key:n.nren},t.createElement("td",null,n.nren),t.createElement("td",null,n.status),t.createElement("td",null,n.lock_description),t.createElement("td",null,t.createElement(Sn,{onClick:function(){return d("/survey/response/".concat(e.year,"/").concat(n.nren))},style:{pointerEvents:"auto"},title:"Open the responses of the NREN."},"open"),t.createElement(Sn,{onClick:function(){return function(e,t){return c.apply(this,arguments)}(e.year,n.nren)},disabled:""==n.lock_description,style:{pointerEvents:"auto"},title:"Remove the lock from the survey so that another person can open the survey for editing. WARNING: The person that currently has the lock will not be abe to save their changes anymore once someone else starts editing!"},"remove lock")))}))))))}))))};function kr(e){return function(e){if(Array.isArray(e))return st(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||at(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Mr(e){return Mr="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Mr(e)}function Lr(e,t,n){return(t=function(e){var t=function(e,t){if("object"!==Mr(e)||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,"string");if("object"!==Mr(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"===Mr(t)?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}const qr=t.forwardRef((({bsPrefix:e,fluid:t=!1,as:n="div",className:r,...o},i)=>{const s=wt(e,"container"),a="string"==typeof t?`-${t}`:"-fluid";return(0,yt.jsx)(n,{ref:i,...o,className:dt()(r,t?`${s}${a}`:s)})}));qr.displayName="Container";const Nr=qr,Ar=t.forwardRef((({bsPrefix:e,className:t,as:n="div",...r},o)=>{const i=wt(e,"row"),s=xt(),a=Pt(),l=`${i}-cols`,u=[];return s.forEach((e=>{const t=r[e];let n;delete r[e],null!=t&&"object"==typeof t?({cols:n}=t):n=t;const o=e!==a?`-${e}`:"";null!=n&&u.push(`${l}${o}-${n}`)})),(0,yt.jsx)(n,{ref:o,...r,className:dt()(t,i,...u)})}));Ar.displayName="Row";const Br=Ar;function Fr(){return(Fr=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/user");case 2:return t=e.sent,e.next=5,t.json();case 5:return n=e.sent,e.abrupt("return",n);case 7:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var Qr={name:"",email:"",permissions:{admin:!1,active:!1},id:"",nrens:[],oidc_sub:"",role:""},zr=(0,t.createContext)({user:Qr,logout:function(){}});const Hr=function(e){var n=e.children,r=lt((0,t.useState)(Qr),2),o=r[0],i=r[1];function s(){return(s=it(ct().mark((function e(){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/logout");case 2:i(Qr);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}return(0,t.useEffect)((function(){(function(){return Fr.apply(this,arguments)})().then((function(e){i(e)}))}),[]),t.createElement(zr.Provider,{value:{user:o,logout:function(){return s.apply(this,arguments)}}},n)};function Ur(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Wr(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?Ur(Object(n),!0).forEach((function(t){Lr(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Ur(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function Jr(){return(Jr=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch("/api/user/list");case 3:return t=e.sent,e.next=6,t.json();case 6:return n=e.sent,e.abrupt("return",n);case 10:return e.prev=10,e.t0=e.catch(0),console.log("handle this better.."),e.abrupt("return",[]);case 14:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}function $r(){return($r=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,fetch("/api/nren/list");case 3:return t=e.sent,e.next=6,t.json();case 6:return n=e.sent,e.abrupt("return",n);case 10:return e.prev=10,e.t0=e.catch(0),console.log("handle this better.."),e.abrupt("return",[]);case 14:case"end":return e.stop()}}),e,null,[[0,10]])})))).apply(this,arguments)}var Gr=function(){var e=it(ct().mark((function e(t,n){var r,o,i,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=Wr({id:t},n),o={method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)},e.next=4,fetch("/api/user/".concat(t),o);case 4:return i=e.sent,e.next=7,i.json();case 7:if(s=e.sent,i.ok){e.next=10;break}throw new Error(s.message);case 10:return e.abrupt("return",s.user);case 11:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),Kr=function(e,t){return e.permissions.active&&!t.permissions.active?-1:!e.permissions.active&&t.permissions.active?1:e.permissions.active&&t.permissions.active?"admin"===e.role&&"admin"!==t.role?1:"admin"!==e.role&&"admin"===t.role?-1:e.name.localeCompare(t.name):e.name.localeCompare(t.name)};const Zr=function(){var e=lt((0,t.useState)([]),2),n=e[0],r=e[1],o=lt((0,t.useState)([]),2),i=o[0],s=o[1],a=(0,t.useContext)(zr).user,l=lt((0,t.useState)({idx:-1,asc:!0}),2),u=l[0],c=l[1],p=lt((0,t.useState)([]),2),d=p[0],h=p[1];(0,t.useEffect)((function(){(function(){return Jr.apply(this,arguments)})().then((function(e){r(e),h(e.sort(Kr))})),function(){return $r.apply(this,arguments)}().then((function(e){s(e.sort((function(e,t){return e.name.localeCompare(t.name)})))}))}),[]);for(var f=function(e,t){var o=n.findIndex((function(e){return e.id===t.id})),i=kr(n),s=e.target.name,a={};a[s]="active"===s?e.target.checked:e.target.value,Gr(t.id,a).then((function(e){i[o]=e,r(i)})).catch((function(e){alert(e.message)}))},g=function(e){var t;e===u.idx||(5===e||0===e)&&-1===u.idx?(5!==e&&0!==e||(e=-1),c({idx:e,asc:!u.asc}),h(kr(d.reverse()))):0===e?(t=Kr,c({idx:-1,asc:!0})):1===e?(t=function(e,t){return e.permissions.active&&!t.permissions.active?-1:!e.permissions.active&&t.permissions.active?1:0},c({idx:e,asc:!0})):2===e?(t=function(e,t){return e.role.localeCompare(t.role)},c({idx:e,asc:!0})):3===e?(t=function(e,t){return e.email.localeCompare(t.email)},c({idx:e,asc:!0})):4===e?(t=function(e,t){return e.name.localeCompare(t.name)},c({idx:e,asc:!0})):5===e?(t=Kr,c({idx:-1,asc:!0})):6===e?(t=function(e,t){return 0===e.nrens.length&&0===t.nrens.length?0:0===e.nrens.length?-1:0===t.nrens.length?1:e.nrens[0].localeCompare(t.nrens[0])},c({idx:e,asc:!0})):(t=Kr,c({idx:e,asc:!0})),h(n.sort(t))},m={},y=0;y<=6;y++)m[y]=u.idx===y?{"aria-sort":u.asc?"ascending":"descending"}:null;return t.createElement(Nr,{style:{maxWidth:"90vw"}},t.createElement(Br,null,t.createElement("h1",null," User Management Page"),t.createElement(Vn,null,t.createElement("thead",null,t.createElement("tr",null,t.createElement("th",ht({},m[0],{onClick:function(){return g(0)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Id"),t.createElement("th",ht({},m[1],{onClick:function(){return g(1)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Active"),t.createElement("th",ht({},m[2],{onClick:function(){return g(2)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Role"),t.createElement("th",ht({},m[3],{onClick:function(){return g(3)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Email"),t.createElement("th",ht({},m[4],{onClick:function(){return g(4)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"Full Name"),t.createElement("th",ht({},m[5],{onClick:function(){return g(5)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"OIDC Sub"),t.createElement("th",ht({},m[6],{onClick:function(){return g(6)},className:"pt-3 sortable",style:{border:"1px solid #ddd"}}),"NREN"))),t.createElement("tbody",null,d.map((function(e){return t.createElement("tr",{key:e.id},t.createElement("td",{style:{border:"1px dotted #ddd"}},e.id),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.id==a.id?"Active":t.createElement("input",{type:"checkbox",name:"active",checked:e.permissions.active,onChange:function(t){return f(t,e)}})),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.id==a.id?e.role.charAt(0).toUpperCase()+e.role.slice(1):t.createElement("select",{name:"role",defaultValue:e.role,onChange:function(t){return f(t,e)}},t.createElement("option",{value:"admin"},"Admin"),t.createElement("option",{value:"user"},"User"),t.createElement("option",{value:"observer"},"Observer"))),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.email),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.name),t.createElement("td",{style:{border:"1px dotted #ddd"}},e.oidc_sub),t.createElement("td",{style:{border:"1px dotted #ddd"}},t.createElement("select",{name:"nren",multiple:!1,defaultValue:e.nrens.length>0?(n=e.nrens[0],null===(r=i.find((function(e){return e.id==n||e.name==n})))||void 0===r?void 0:r.id):void 0,onChange:function(t){return f(t,e)}},t.createElement("option",null,"Select NREN"),i.map((function(e){return t.createElement("option",{key:e.id,value:e.id},e.name)})))));var n,r}))))))};var Yr=n(535),Xr=n(352);function eo(e,t){if(0==t.column.indexValue&&"item"in t.row){var n,r,o=t.row.item;void 0!==o.customDescription&&(null===(n=t.htmlElement.parentElement)||void 0===n||n.children[0].children[0].setAttribute("description",o.customDescription),null===(r=t.htmlElement.parentElement)||void 0===r||r.children[0].children[0].classList.add("survey-tooltip"))}}function to(e){var t=e[0];if(void 0===t||null==t||""==t)return!0;try{var n=new URL(t);return"http:"===n.protocol||"https:"===n.protocol}catch(e){return!1}}function no(e,t){t.question.hideCheckboxLabels&&(t.cssClasses.root+=" hidden-checkbox-labels")}function ro(e,t){var n,r='[data-name="'+t.question.name+'"]',o=null===(n=document.querySelector(r))||void 0===n?void 0:n.querySelector("h5");o&&!o.classList.contains("sv-header-flex")&&t.question.updateElementCss()}function oo(e,t,n){var r;n.verificationStatus.set(e.name,t);var o=document.createElement("button");o.type="button",o.className="sv-action-bar-item verification",o.innerHTML=t,t==Vr.Unverified?(o.innerHTML="No change from previous year",o.className+=" verification-required",o.onclick=function(){"display"!=n.mode&&(e.validate(),oo(e,Vr.Verified,n))}):(o.innerHTML="Answer updated",o.className+=" verification-ok");var i='[data-name="'+e.name+'"]',s=null===(r=document.querySelector(i))||void 0===r?void 0:r.querySelector("h5"),a=null==s?void 0:s.querySelector(".verification");a?a.replaceWith(o):null==s||s.appendChild(o)}const io=function(e){var n=e.surveyModel,r=(0,t.useCallback)((function(e,t){var r=n.verificationStatus.get(t.question.name);r&&oo(t.question,r,n)}),[n]),o=(0,t.useCallback)((function(e,t){n.verificationStatus.get(t.question.name)==Vr.Unverified&&oo(t.question,Vr.Edited,n)}),[n]);return Yr.FunctionFactory.Instance.hasFunction("validateWebsiteUrl")||Yr.FunctionFactory.Instance.register("validateWebsiteUrl",to),n.css.question.title.includes("sv-header-flex")||(n.css.question.title="sv-title sv-question__title sv-header-flex",n.css.question.titleOnError="sv-question__title--error sv-error-color-fix"),n.onAfterRenderQuestion.hasFunc(r)||(n.onAfterRenderQuestion.add(r),n.onAfterRenderQuestion.add(ro)),n.onValueChanged.hasFunc(o)||n.onValueChanged.add(o),n.onUpdateQuestionCssClasses.hasFunc(no)||n.onUpdateQuestionCssClasses.add(no),n.onMatrixAfterCellRender.hasFunc(eo)||n.onMatrixAfterCellRender.add(eo),t.createElement(Xr.Survey,{model:n})},so=t.forwardRef(((e,t)=>{const[{className:n,...r},{as:o="div",bsPrefix:i,spans:s}]=function({as:e,bsPrefix:t,className:n,...r}){t=wt(t,"col");const o=xt(),i=Pt(),s=[],a=[];return o.forEach((e=>{const n=r[e];let o,l,u;delete r[e],"object"==typeof n&&null!=n?({span:o,offset:l,order:u}=n):o=n;const c=e!==i?`-${e}`:"";o&&s.push(!0===o?`${t}${c}`:`${t}${c}-${o}`),null!=u&&a.push(`order${c}-${u}`),null!=l&&a.push(`offset${c}-${l}`)})),[{...r,className:dt()(n,...s,...a)},{as:e,bsPrefix:t,spans:s}]}(e);return(0,yt.jsx)(o,{...r,ref:t,className:dt()(n,!s.length&&i)})}));so.displayName="Col";const ao=so;function lo(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function uo(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?lo(Object(n),!0).forEach((function(t){Lr(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):lo(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}const co=function(e){var n=e.surveyModel,r=e.pageNoSetter,o=lt((0,t.useState)([]),2),i=o[0],s=o[1],a=function(e){return!(null===e.value||void 0===e.value||""===e.value||"checkbox"===e.getType()&&0==e.value.length||"multipletext"===e.getType()&&(1===Object.keys(e.value).length&&void 0===Object.values(e.value)[0]||0===Object.keys(e.value).length))};(0,t.useEffect)((function(){var e=function(e){if(e&&e.pages){var t=[];e.pages.forEach((function(n){var r=n.questions.filter((function(e){return e.startWithNewLine})),o=r.length,i=r.filter(a).length,s=o-i,l=i/o;t.push({completionPercentage:100*l,unansweredPercentage:s/o*100,totalPages:e.pages.length,pageTitle:n.title})})),s(t)}};n.onValueChanged.add((function(t){e(t)})),e(n)}),[n]);var l={height:"0.5rem",transition:"width 0.3s ease"};return t.createElement(Nr,{className:"survey-progress"},t.createElement(Br,null,i.map((function(e,o){return t.createElement(ao,{xs:12,md:!0,key:o,onClick:function(){return r(o)},style:{cursor:"pointer",margin:"0.5rem"}},t.createElement("div",null,t.createElement("span",{style:{whiteSpace:"nowrap",fontSize:"1.5rem",marginRight:"0.25rem",fontWeight:"bold",color:"#2db394"}},o+1),t.createElement("span",{style:uo({whiteSpace:"nowrap"},n.currentPageNo==o&&{fontWeight:"bold"})},e.pageTitle),t.createElement("div",{style:{display:"flex",flexWrap:"wrap"}},t.createElement("div",{style:uo(uo({},l),{},{width:"".concat(e.completionPercentage,"%"),backgroundColor:"#262261"})}),t.createElement("div",{style:uo(uo({},l),{},{width:"".concat(e.unansweredPercentage,"%"),backgroundColor:"#cdcdcd"})}))))}))))},po=function(e){var n=e.surveyModel,r=e.surveyActions,o=e.year,i=e.nren,s=e.children,a=lt((0,t.useState)(0),2),l=a[0],u=a[1],c=lt((0,t.useState)(!1),2),p=c[0],d=c[1],h=lt((0,t.useState)(""),2),f=h[0],g=h[1],m=lt((0,t.useState)(""),2),y=m[0],v=m[1],b=(0,t.useContext)(zr).user,C=(0,t.useCallback)((function(){d("edit"==n.mode),g(n.lockedBy),u(n.currentPageNo),v(n.status)}),[n]);(0,t.useEffect)((function(){C()}),[C]);var w=function(e){u(e),n.currentPageNo=e},x=function(){w(n.currentPageNo+1)},P=function(){var e=it(ct().mark((function e(t){return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,r[t]();case 2:C();case 3:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),S=function(e,t){return V(e,(function(){return P(t)}))},V=function(e,n){return t.createElement("button",{className:"sv-btn sv-btn--navigation",onClick:n},e)},E="Save and stop editing",O="Save progress",T="Start editing",_="Complete Survey",R=function(){return t.createElement("div",{className:"survey-edit-buttons-block"},!p&&!f&&n.editAllowed&&S(T,"startEdit"),!p&&f&&f==b.name&&S("Discard any unsaved changes and release your lock","releaseLock"),p&&y==Er.started&&S(O,"save"),p&&y==Er.started&&S(E,"saveAndStopEdit"),p&&l===n.visiblePages.length-1&&S(_,"complete"),l!==n.visiblePages.length-1&&V("Next Section",x))},I=parseInt(o);return t.createElement(Nr,null,t.createElement(Br,{className:"survey-content"},t.createElement("h2",null,t.createElement("span",{className:"survey-title"},o," Compendium Survey "),t.createElement("span",{className:"survey-title-nren"}," ",i," "),t.createElement("span",null," - ",y)),t.createElement("p",{style:{marginTop:"1rem"}},"To get started, click “",T,"” to end read-only mode. This is only possible when nobody else from your NREN is currently working on the survey.",t.createElement("br",null),"Where available, the survey questions are pre-filled with answers from the previous year. The survey asks about the past year, i.e. the ",o," survey asks about data from ",I-1," (or ",I-1,"/",o," if your NRENs financial year does not match the calendar year). You can edit the prefilled answer to provide new information, or press the “no change from previous year” button.",t.createElement("br",null),"Press the “",O,"“ or “",E,"“ button to save all answers in the survey. When you reach the last section of the survey (Services), you will find a “",_,"“ button which saves all answers in the survey and lets the Compendium team know that your answers are ready to be published.",t.createElement("br",null),"As long as the survey remains open, any Compendium administrator from your NREN can add answers or amend existing ones, even after using the “",_,"“ button. Different people from your NREN can contribute to the survey if needed.",t.createElement("br",null),"Some fields require specific data, such as numerical data, valid http-addresses, and in some questions, the answer has to add up to 100%. If an answer does not fulfil the set criteria, the question will turn pink and an error message will appear. Fields can be left blank if you prefer not to answer a question.",t.createElement("br",null),"If you notice any errors after the survey was closed, please contact us for correcting those."),t.createElement("p",null,"Thank you for taking the time to fill in the ",o," Compendium Survey. Any questions or requests can be sent to ",t.createElement("a",{href:"mailto:Partner-Relations@geant.org"},t.createElement("span",null,"Partner-Relations@geant.org"))),p&&t.createElement(t.Fragment,null,t.createElement("br",null),t.createElement("b",null,"Remember to click “",E,"” before leaving the page."))),t.createElement(Br,null,R()),t.createElement(Br,{className:"survey-content"},!p&&t.createElement("div",{className:"survey-edit-explainer"},!f&&n.editAllowed&&"The survey is in read-only mode; click the “Start editing“ button to begin editing the answers.",!f&&!n.editAllowed&&"The survey is in read-only mode and can not be edited by you.",f&&f!=b.name&&"The survey is in read-only mode and currently being edited by: "+f+". To start editing the survey, ask them to complete their edits.",f&&f==b.name&&'The survey is in read-only mode because you started editing in another tab, browser or device. To start editing the survey, either complete those edits or click the "Discard any unsaved changes" button.')),t.createElement(Br,null,t.createElement(co,{surveyModel:n,pageNoSetter:w}),s),t.createElement(Br,null,R()))},ho=function(e){var n=e.when,r=e.onPageExit;return function(e){let{router:n}=Qe(Be.UseBlocker),r=ze(Fe.UseBlocker),[o]=t.useState((()=>String(++Ue))),i=t.useCallback((t=>!!e()),[e]);n.getBlocker(o,i);t.useEffect((()=>()=>n.deleteBlocker(o)),[n,o]),r.blockers.get(o)}((function(){if(n()){var t=window.confirm(e.message);return t&&r(),!t}return!1})),t.createElement("div",null)};Yr.Serializer.addProperty("itemvalue","customDescription:text"),Yr.Serializer.addProperty("question","hideCheckboxLabels:boolean");const fo=function(e){var n=e.loadFrom,r=lt((0,t.useState)(),2),o=r[0],i=r[1],s=function(){let{matches:e}=t.useContext(Te),n=e[e.length-1];return n?n.params:{}}(),a=s.year,l=s.nren,u=lt((0,t.useState)("loading survey..."),2),c=u[0],p=u[1],d=(0,t.useCallback)((function(e){return e.preventDefault(),e.returnValue=""}),[]),h=(0,t.useCallback)((function(){window.navigator.sendBeacon("/api/response/unlock/"+a+"/"+l)}),[]),f=(0,t.useCallback)((function(){window.navigator.sendBeacon("/api/response/unlock/"+a+"/"+l),removeEventListener("beforeunload",d,{capture:!0}),removeEventListener("pagehide",h)}),[]);if((0,t.useEffect)((function(){function e(){return(e=it(ct().mark((function e(){var t,r,o,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch(n+a+(l?"/"+l:""));case 2:return t=e.sent,e.next=5,t.json();case 5:if(r=e.sent,t.ok){e.next=12;break}if(!("message"in r)){e.next=11;break}throw new Error(r.message);case 11:throw new Error("Request failed with status ".concat(t.status));case 12:for(s in(o=new Yr.Model(r.model)).setVariable("surveyyear",a),o.setVariable("previousyear",parseInt(a)-1),o.showNavigationButtons=!1,o.requiredText="",o.verificationStatus=new Map,r.verification_status)o.verificationStatus.set(s,r.verification_status[s]);o.data=r.data,o.clearIncorrectValues(!0),o.currentPageNo=r.page,o.mode=r.mode,o.lockedBy=r.locked_by,o.status=r.status,o.editAllowed=r.edit_allowed,i(o);case 27:case"end":return e.stop()}}),e)})))).apply(this,arguments)}(function(){return e.apply(this,arguments)})().catch((function(e){return p("Error when loading survey: "+e.message)}))}),[]),!o)return c;var g,m,y,v,b,C=function(){var e=it(ct().mark((function e(t,n){var r,i,s;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(l){e.next=2;break}return e.abrupt("return","Saving not available in inpect/try mode");case 2:return r={lock_uuid:t.lockUUID,new_state:n,data:t.data,page:t.currentPageNo,verification_status:Object.fromEntries(t.verificationStatus)},e.prev=3,e.next=6,fetch("/api/response/save/"+a+"/"+l,{method:"POST",headers:{"Content-Type":"application/json; charset=utf-8"},body:JSON.stringify(r)});case 6:return i=e.sent,e.next=9,i.json();case 9:if(s=e.sent,i.ok){e.next=12;break}return e.abrupt("return",s.message);case 12:o.mode=s.mode,o.lockedBy=s.locked_by,o.status=s.status,e.next=20;break;case 17:return e.prev=17,e.t0=e.catch(3),e.abrupt("return","Unknown Error: "+e.t0.message);case 20:case"end":return e.stop()}}),e,null,[[3,17]])})));return function(t,n){return e.apply(this,arguments)}}(),w=function(e){var t="",n=function(e,n){e.verificationStatus.get(n.name)==Vr.Unverified&&(""==t&&(t=n.name),n.error='Please verify that last years data is correct by editing the answer or pressing the "No change from previous year" button!')};o.onValidateQuestion.add(n);var r=e();return o.onValidateQuestion.remove(n),r||Sr("Validation failed!"),r},x={save:(b=it(ct().mark((function e(){var t;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,C(o,"editing");case 2:t=e.sent,Sr(t?"Failed saving survey: "+t:"Survey saved!");case 4:case"end":return e.stop()}}),e)}))),function(){return b.apply(this,arguments)}),complete:(v=it(ct().mark((function e(){var t;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!w(o.validate.bind(o,!0,!0))){e.next=6;break}return e.next=4,C(o,"completed");case 4:(t=e.sent)?Sr("Failed completing survey: "+t):(Sr("Survey completed!"),removeEventListener("beforeunload",d,{capture:!0}),removeEventListener("pagehide",h));case 6:case"end":return e.stop()}}),e)}))),function(){return v.apply(this,arguments)}),saveAndStopEdit:(y=it(ct().mark((function e(){var t;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,C(o,"readonly");case 2:(t=e.sent)?Sr("Failed saving survey: "+t):(Sr("Survey saved!"),removeEventListener("beforeunload",d,{capture:!0}),removeEventListener("pagehide",h));case 4:case"end":return e.stop()}}),e)}))),function(){return y.apply(this,arguments)}),startEdit:(m=it(ct().mark((function e(){var t,n,r;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/response/lock/"+a+"/"+l,{method:"POST"});case 2:return t=e.sent,e.next=5,t.json();case 5:if(n=e.sent,t.ok){e.next=9;break}return Sr("Failed starting edit: "+n.message),e.abrupt("return");case 9:for(r in addEventListener("pagehide",h),addEventListener("beforeunload",d,{capture:!0}),n.verification_status)o.verificationStatus.set(r,n.verification_status[r]);o.data=n.data,o.clearIncorrectValues(!0),o.mode=n.mode,o.lockedBy=n.locked_by,o.lockUUID=n.lock_uuid,o.status=n.status;case 18:case"end":return e.stop()}}),e)}))),function(){return m.apply(this,arguments)}),releaseLock:(g=it(ct().mark((function e(){var t,n;return ct().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/response/unlock/"+a+"/"+l,{method:"POST"});case 2:return t=e.sent,e.next=5,t.json();case 5:if(n=e.sent,t.ok){e.next=9;break}return Sr("Failed releasing lock: "+n.message),e.abrupt("return");case 9:o.mode=n.mode,o.lockedBy=n.locked_by,o.status=n.status;case 12:case"end":return e.stop()}}),e)}))),function(){return g.apply(this,arguments)}),validatePage:function(){w(o.validatePage.bind(o))&&Sr("Page validation successful!")}};return t.createElement(Nr,{className:"survey-container"},t.createElement(Pr,null),t.createElement(ho,{message:"Are you sure you want to leave this page? Information you've entered may not be saved.",when:function(){return"edit"==o.mode&&!!l},onPageExit:f}),t.createElement(po,{surveyModel:o,surveyActions:x,year:a,nren:l},t.createElement(io,{surveyModel:o})))},go=n.p+"9ab20ac1d835b50b2e01.svg",mo=function(){return t.createElement("div",{className:"external-page-nav-bar"},t.createElement(Nr,null,t.createElement(Br,null,t.createElement(ao,{xs:10},t.createElement("div",{className:"nav-wrapper"},t.createElement("nav",{className:"header-nav"},t.createElement("a",{href:"https://geant.org/"},t.createElement("img",{src:go})),t.createElement("ul",null,t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://network.geant.org/"},"NETWORK")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://geant.org/services/"},"SERVICES")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://community.geant.org/"},"COMMUNITY")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://tnc23.geant.org/"},"TNC")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://geant.org/projects/"},"PROJECTS")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://connect.geant.org/"},"CONNECT")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://impact.geant.org/"},"IMPACT")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://careers.geant.org/"},"CAREERS")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://about.geant.org/"},"ABOUT")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://connect.geant.org/community-news"},"NEWS")),t.createElement("li",null,t.createElement("a",{className:"nav-link-entry",href:"https://resources.geant.org/"},"RESOURCES")))))))))},yo=function(){var e,n=(0,t.useContext)(zr).user,r=je(),o=!!n.id,i=!!o&&!!n.nrens.length,s=i?n.nrens[0]:"",a=!!o&&n.permissions.admin,l=!!o&&"observer"===n.role,u=function(){var e=lt((0,t.useState)(),2),n=e[0],r=e[1];return(0,t.useEffect)((function(){Tr().then((function(e){r(e[0])}))}),[]),t.createElement(Vn,{striped:!0,bordered:!0,responsive:!0},t.createElement("thead",null,t.createElement("tr",null,t.createElement("th",null,"(N)REN"),t.createElement("th",null,"Link"),t.createElement("th",null,"Survey Status"))),t.createElement("tbody",null,n&&n.responses.map((function(e){return t.createElement("tr",{key:e.nren},t.createElement("td",null,e.nren),t.createElement("td",null,t.createElement(tt,{to:"/survey/response/".concat(n.year,"/").concat(e.nren)},t.createElement("span",null,"Navigate to survey"))),t.createElement("td",null,e.status))}))))};return t.createElement(Nr,{className:"py-5 grey-container"},t.createElement(Br,null,t.createElement("div",{className:"center-text"},t.createElement("h1",{className:"geant-header"},"THE GÉANT COMPENDIUM OF NRENS SURVEY"),t.createElement("div",{className:"wordwrap pt-4",style:{maxWidth:"75rem"}},t.createElement("p",{style:{textAlign:"left"}},"Hello,",t.createElement("br",null),"Welcome to the GÉANT Compendium Survey. (N)REN Compendium administrators can login via Single Sign On (SSO) ",t.createElement("a",{href:"/login"},"here"),", which will complete their registration to fill in the 2023 Compendium survey. This will send a notification to the Compendium administration team and they will assign you to your (N)REN.",t.createElement("br",null),"Once this step has been completed, you will receive an email from the administration team. We aim to get back to you the same working day, but sometimes may take a little longer.",t.createElement("br",null),"If you are not sure whether you are a Compendium Administrator for your (N)REN, please contact your GÉANT Partner Relations relationship manager.",t.createElement("br",null),"Thank you."),t.createElement("span",null,"Current registration status:"),t.createElement("br",null),t.createElement("br",null),a?t.createElement("ul",null,t.createElement("li",null,t.createElement("span",null,"You are logged in as a Compendium Administrator")),t.createElement("li",null,t.createElement("span",null,"Click ",t.createElement(tt,{to:"/survey/admin/surveys"},"here")," to access the survey management page.")),t.createElement("li",null,t.createElement("span",null,"Click ",t.createElement(tt,{to:"/survey/admin/users"},"here")," to access the user management page."))):t.createElement("ul",null,!a&&!l&&i&&(e=(new Date).getFullYear(),r("/survey/response/".concat(e,"/").concat(s)),t.createElement(t.Fragment,null)),o?t.createElement("li",null,t.createElement("span",null,"You are logged in")):t.createElement("li",null,t.createElement("span",null,"You are not logged in")),o&&!l&&!i&&t.createElement("li",null,t.createElement("span",null,"Your access to the survey has not yet been approved")),o&&!l&&!i&&t.createElement("li",null,t.createElement("span",null,"Once you have been approved, you will immediately be directed to the relevant survey upon visiting this page")),o&&l&&t.createElement("li",null,t.createElement("span",null,"You have read-only access to the following surveys:"))),o&&l&&t.createElement(u,null)))))};var vo,bo=(vo=[{path:"survey/admin/surveys",element:t.createElement(jr,null)},{path:"survey/admin/users",element:t.createElement(Zr,null)},{path:"survey/admin/inspect/:year",element:t.createElement(fo,{loadFrom:"/api/response/inspect/"})},{path:"survey/admin/try/:year",element:t.createElement(fo,{loadFrom:"/api/response/try/"})},{path:"survey/response/:year/:nren",element:t.createElement(fo,{loadFrom:"/api/response/load/"})},{path:"*",element:t.createElement(yo,null)}],function(t){let n;if(s(t.routes.length>0,"You must provide a non-empty routes array to createRouter"),t.mapRouteProperties)n=t.mapRouteProperties;else if(t.detectErrorBoundary){let e=t.detectErrorBoundary;n=t=>({hasErrorBoundary:e(t)})}else n=Z;let r,i={},l=f(t.routes,n,void 0,i),c=t.basename||"/",p=o({v7_normalizeFormMethod:!1,v7_prependBasename:!1},t.future),h=null,m=new Set,y=null,v=null,b=null,C=null!=t.hydrationData,w=g(l,t.history.location,c),x=null;if(null==w){let e=pe(404,{pathname:t.history.location.pathname}),{matches:n,route:r}=ce(l);w=n,x={[r.id]:e}}let P,S,V=!(w.some((e=>e.route.lazy))||w.some((e=>e.route.loader))&&null==t.hydrationData),E={historyAction:t.history.action,location:t.history.location,matches:w,initialized:V,navigation:U,restoreScrollPosition:null==t.hydrationData&&null,preventScrollReset:!1,revalidation:"idle",loaderData:t.hydrationData&&t.hydrationData.loaderData||{},actionData:t.hydrationData&&t.hydrationData.actionData||null,errors:t.hydrationData&&t.hydrationData.errors||x,fetchers:new Map,blockers:new Map},O=e.Pop,T=!1,R=!1,I=!1,D=[],j=[],k=new Map,M=0,L=-1,q=new Map,N=new Set,A=new Map,B=new Map,F=new Map,Q=!1;function z(e){E=o({},E,e),m.forEach((e=>e(E)))}function te(n,i){var s,a;let u,c=null!=E.actionData&&null!=E.navigation.formMethod&&ye(E.navigation.formMethod)&&"loading"===E.navigation.state&&!0!==(null==(s=n.state)?void 0:s._isRedirect);u=i.actionData?Object.keys(i.actionData).length>0?i.actionData:null:c?E.actionData:null;let p=i.loaderData?le(E.loaderData,i.loaderData,i.matches||[],i.errors):E.loaderData;for(let[e]of F)_e(e);let d=!0===T||null!=E.navigation.formMethod&&ye(E.navigation.formMethod)&&!0!==(null==(a=n.state)?void 0:a._isRedirect);r&&(l=r,r=void 0),z(o({},i,{actionData:u,loaderData:p,historyAction:O,location:n,initialized:!0,navigation:U,revalidation:"idle",restoreScrollPosition:je(n,i.matches||E.matches),preventScrollReset:d,blockers:new Map(E.blockers)})),R||O===e.Pop||(O===e.Push?t.history.push(n,n.state):O===e.Replace&&t.history.replace(n,n.state)),O=e.Pop,T=!1,R=!1,I=!1,D=[],j=[]}async function ne(s,a,u){S&&S.abort(),S=null,O=s,R=!0===(u&&u.startUninterruptedRevalidation),function(e,t){if(y&&v&&b){let n=t.map((e=>we(e,E.loaderData))),r=v(e,n)||e.key;y[r]=b()}}(E.location,E.matches),T=!0===(u&&u.preventScrollReset);let p=r||l,h=u&&u.overrideNavigation,f=g(p,a,c);if(!f){let e=pe(404,{pathname:a.pathname}),{matches:t,route:n}=ce(p);return De(),void te(a,{matches:t,loaderData:{},errors:{[n.id]:e}})}if(E.initialized&&function(e,t){return e.pathname===t.pathname&&e.search===t.search&&(""===e.hash?""!==t.hash:e.hash===t.hash||""!==t.hash)}(E.location,a)&&!(u&&u.submission&&ye(u.submission.formMethod)))return void te(a,{matches:f});S=new AbortController;let m,C,w=ie(t.history,a,S.signal,u&&u.submission);if(u&&u.pendingError)C={[ue(f).route.id]:u.pendingError};else if(u&&u.submission&&ye(u.submission.formMethod)){let t=await async function(t,r,s,a,l){let u;Ce(),z({navigation:o({state:"submitting",location:r},s)});let p=xe(a,r);if(p.route.action||p.route.lazy){if(u=await oe("action",t,p,a,i,n,c),t.signal.aborted)return{shortCircuited:!0}}else u={type:d.error,error:pe(405,{method:t.method,pathname:r.pathname,routeId:p.route.id})};if(me(u)){let e;return e=l&&null!=l.replace?l.replace:u.location===E.location.pathname+E.location.search,await se(E,u,{submission:s,replace:e}),{shortCircuited:!0}}if(ge(u)){let t=ue(a,p.route.id);return!0!==(l&&l.replace)&&(O=e.Push),{pendingActionData:{},pendingActionError:{[t.route.id]:u.error}}}if(fe(u))throw pe(400,{type:"defer-action"});return{pendingActionData:{[p.route.id]:u.data}}}(w,a,u.submission,f,{replace:u.replace});if(t.shortCircuited)return;m=t.pendingActionData,C=t.pendingActionError,h=o({state:"loading",location:a},u.submission),w=new Request(w.url,{signal:w.signal})}let{shortCircuited:x,loaderData:P,errors:V}=await async function(e,n,i,s,a,u,p,d,h){let f=s;f||(f=o({state:"loading",location:n,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0},a));let g=a||u?a||u:f.formMethod&&f.formAction&&f.formData&&f.formEncType?{formMethod:f.formMethod,formAction:f.formAction,formData:f.formData,formEncType:f.formEncType}:void 0,m=r||l,[y,v]=ee(t.history,E,i,g,n,I,D,j,A,m,c,d,h);if(De((e=>!(i&&i.some((t=>t.route.id===e)))||y&&y.some((t=>t.route.id===e)))),0===y.length&&0===v.length){let e=Oe();return te(n,o({matches:i,loaderData:{},errors:h||null},d?{actionData:d}:{},e?{fetchers:new Map(E.fetchers)}:{})),{shortCircuited:!0}}if(!R){v.forEach((e=>{let t=E.fetchers.get(e.key),n={state:"loading",data:t&&t.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(e.key,n)}));let e=d||E.actionData;z(o({navigation:f},e?0===Object.keys(e).length?{actionData:null}:{actionData:e}:{},v.length>0?{fetchers:new Map(E.fetchers)}:{}))}L=++M,v.forEach((e=>{e.controller&&k.set(e.key,e.controller)}));let b=()=>v.forEach((e=>Ve(e.key)));S&&S.signal.addEventListener("abort",b);let{results:C,loaderResults:w,fetcherResults:x}=await he(E.matches,i,y,v,e);if(e.signal.aborted)return{shortCircuited:!0};S&&S.signal.removeEventListener("abort",b),v.forEach((e=>k.delete(e.key)));let P=de(C);if(P)return await se(E,P,{replace:p}),{shortCircuited:!0};let{loaderData:V,errors:O}=ae(E,i,y,w,h,v,x,B);B.forEach(((e,t)=>{e.subscribe((n=>{(n||e.done)&&B.delete(t)}))}));let T=Oe(),_=Te(L);return o({loaderData:V,errors:O},T||_||v.length>0?{fetchers:new Map(E.fetchers)}:{})}(w,a,f,h,u&&u.submission,u&&u.fetcherSubmission,u&&u.replace,m,C);x||(S=null,te(a,o({matches:f},m?{actionData:m}:{},{loaderData:P,errors:V})))}function re(e){return E.fetchers.get(e)||W}async function se(n,r,i){var a;let{submission:l,replace:p,isFetchActionRedirect:d}=void 0===i?{}:i;r.revalidate&&(I=!0);let h=u(n.location,r.location,o({_isRedirect:!0},d?{_isFetchActionRedirect:!0}:{}));if(s(h,"Expected a location on the redirect navigation"),$.test(r.location)&&G&&void 0!==(null==(a=window)?void 0:a.location)){let e=t.history.createURL(r.location),n=null==_(e.pathname,c);if(window.location.origin!==e.origin||n)return void(p?window.location.replace(r.location):window.location.assign(r.location))}S=null;let f=!0===p?e.Replace:e.Push,{formMethod:g,formAction:m,formEncType:y,formData:v}=n.navigation;!l&&g&&m&&v&&y&&(l={formMethod:g,formAction:m,formEncType:y,formData:v}),H.has(r.status)&&l&&ye(l.formMethod)?await ne(f,h,{submission:o({},l,{formAction:r.location}),preventScrollReset:T}):d?await ne(f,h,{overrideNavigation:{state:"loading",location:h,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0},fetcherSubmission:l,preventScrollReset:T}):await ne(f,h,{overrideNavigation:{state:"loading",location:h,formMethod:l?l.formMethod:void 0,formAction:l?l.formAction:void 0,formEncType:l?l.formEncType:void 0,formData:l?l.formData:void 0},preventScrollReset:T})}async function he(e,r,o,s,a){let l=await Promise.all([...o.map((e=>oe("loader",a,e,r,i,n,c))),...s.map((e=>e.matches&&e.match&&e.controller?oe("loader",ie(t.history,e.path,e.controller.signal),e.match,e.matches,i,n,c):{type:d.error,error:pe(404,{pathname:e.path})}))]),u=l.slice(0,o.length),p=l.slice(o.length);return await Promise.all([ve(e,o,u,u.map((()=>a.signal)),!1,E.loaderData),ve(e,s.map((e=>e.match)),p,s.map((e=>e.controller?e.controller.signal:null)),!0)]),{results:l,loaderResults:u,fetcherResults:p}}function Ce(){I=!0,D.push(...De()),A.forEach(((e,t)=>{k.has(t)&&(j.push(t),Ve(t))}))}function Pe(e,t,n){let r=ue(E.matches,t);Se(e),z({errors:{[r.route.id]:n},fetchers:new Map(E.fetchers)})}function Se(e){k.has(e)&&Ve(e),A.delete(e),q.delete(e),N.delete(e),E.fetchers.delete(e)}function Ve(e){let t=k.get(e);s(t,"Expected fetch controller: "+e),t.abort(),k.delete(e)}function Ee(e){for(let t of e){let e={state:"idle",data:re(t).data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(t,e)}}function Oe(){let e=[],t=!1;for(let n of N){let r=E.fetchers.get(n);s(r,"Expected fetcher: "+n),"loading"===r.state&&(N.delete(n),e.push(n),t=!0)}return Ee(e),t}function Te(e){let t=[];for(let[n,r]of q)if(r<e){let e=E.fetchers.get(n);s(e,"Expected fetcher: "+n),"loading"===e.state&&(Ve(n),q.delete(n),t.push(n))}return Ee(t),t.length>0}function _e(e){E.blockers.delete(e),F.delete(e)}function Re(e,t){let n=E.blockers.get(e)||J;s("unblocked"===n.state&&"blocked"===t.state||"blocked"===n.state&&"blocked"===t.state||"blocked"===n.state&&"proceeding"===t.state||"blocked"===n.state&&"unblocked"===t.state||"proceeding"===n.state&&"unblocked"===t.state,"Invalid blocker state transition: "+n.state+" -> "+t.state),E.blockers.set(e,t),z({blockers:new Map(E.blockers)})}function Ie(e){let{currentLocation:t,nextLocation:n,historyAction:r}=e;if(0===F.size)return;F.size>1&&a(!1,"A router only supports one blocker at a time");let o=Array.from(F.entries()),[i,s]=o[o.length-1],l=E.blockers.get(i);return l&&"proceeding"===l.state?void 0:s({currentLocation:t,nextLocation:n,historyAction:r})?i:void 0}function De(e){let t=[];return B.forEach(((n,r)=>{e&&!e(r)||(n.cancel(),t.push(r),B.delete(r))})),t}function je(e,t){if(y&&v&&b){let n=t.map((e=>we(e,E.loaderData))),r=v(e,n)||e.key,o=y[r];if("number"==typeof o)return o}return null}return P={get basename(){return c},get state(){return E},get routes(){return l},initialize:function(){return h=t.history.listen((e=>{let{action:n,location:r,delta:o}=e;if(Q)return void(Q=!1);a(0===F.size||null!=o,"You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL.");let i=Ie({currentLocation:E.location,nextLocation:r,historyAction:n});return i&&null!=o?(Q=!0,t.history.go(-1*o),void Re(i,{state:"blocked",location:r,proceed(){Re(i,{state:"proceeding",proceed:void 0,reset:void 0,location:r}),t.history.go(o)},reset(){_e(i),z({blockers:new Map(P.state.blockers)})}})):ne(n,r)})),E.initialized||ne(e.Pop,E.location),P},subscribe:function(e){return m.add(e),()=>m.delete(e)},enableScrollRestoration:function(e,t,n){if(y=e,b=t,v=n||(e=>e.key),!C&&E.navigation===U){C=!0;let e=je(E.location,E.matches);null!=e&&z({restoreScrollPosition:e})}return()=>{y=null,b=null,v=null}},navigate:async function n(r,i){if("number"==typeof r)return void t.history.go(r);let s=Y(E.location,E.matches,c,p.v7_prependBasename,r,null==i?void 0:i.fromRouteId,null==i?void 0:i.relative),{path:a,submission:l,error:d}=X(p.v7_normalizeFormMethod,!1,s,i),h=E.location,f=u(E.location,a,i&&i.state);f=o({},f,t.history.encodeLocation(f));let g=i&&null!=i.replace?i.replace:void 0,m=e.Push;!0===g?m=e.Replace:!1===g||null!=l&&ye(l.formMethod)&&l.formAction===E.location.pathname+E.location.search&&(m=e.Replace);let y=i&&"preventScrollReset"in i?!0===i.preventScrollReset:void 0,v=Ie({currentLocation:h,nextLocation:f,historyAction:m});if(!v)return await ne(m,f,{submission:l,pendingError:d,preventScrollReset:y,replace:i&&i.replace});Re(v,{state:"blocked",location:f,proceed(){Re(v,{state:"proceeding",proceed:void 0,reset:void 0,location:f}),n(r,i)},reset(){_e(v),z({blockers:new Map(E.blockers)})}})},fetch:function(e,a,u,d){if(K)throw new Error("router.fetch() was called during the server render, but it shouldn't be. You are likely calling a useFetcher() method in the body of your component. Try moving it to a useEffect or a callback.");k.has(e)&&Ve(e);let h=r||l,f=Y(E.location,E.matches,c,p.v7_prependBasename,u,a,null==d?void 0:d.relative),m=g(h,f,c);if(!m)return void Pe(e,a,pe(404,{pathname:f}));let{path:y,submission:v}=X(p.v7_normalizeFormMethod,!0,f,d),b=xe(m,y);T=!0===(d&&d.preventScrollReset),v&&ye(v.formMethod)?async function(e,a,u,p,d,h){if(Ce(),A.delete(e),!p.route.action&&!p.route.lazy){let t=pe(405,{method:h.formMethod,pathname:u,routeId:a});return void Pe(e,a,t)}let f=E.fetchers.get(e),m=o({state:"submitting"},h,{data:f&&f.data," _hasFetcherDoneAnything ":!0});E.fetchers.set(e,m),z({fetchers:new Map(E.fetchers)});let y=new AbortController,v=ie(t.history,u,y.signal,h);k.set(e,y);let b=await oe("action",v,p,d,i,n,c);if(v.signal.aborted)return void(k.get(e)===y&&k.delete(e));if(me(b)){k.delete(e),N.add(e);let t=o({state:"loading"},h,{data:void 0," _hasFetcherDoneAnything ":!0});return E.fetchers.set(e,t),z({fetchers:new Map(E.fetchers)}),se(E,b,{submission:h,isFetchActionRedirect:!0})}if(ge(b))return void Pe(e,a,b.error);if(fe(b))throw pe(400,{type:"defer-action"});let C=E.navigation.location||E.location,w=ie(t.history,C,y.signal),x=r||l,P="idle"!==E.navigation.state?g(x,E.navigation.location,c):E.matches;s(P,"Didn't find any matches after fetcher action");let V=++M;q.set(e,V);let T=o({state:"loading",data:b.data},h,{" _hasFetcherDoneAnything ":!0});E.fetchers.set(e,T);let[_,R]=ee(t.history,E,P,h,C,I,D,j,A,x,c,{[p.route.id]:b.data},void 0);R.filter((t=>t.key!==e)).forEach((e=>{let t=e.key,n=E.fetchers.get(t),r={state:"loading",data:n&&n.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(t,r),e.controller&&k.set(t,e.controller)})),z({fetchers:new Map(E.fetchers)});let F=()=>R.forEach((e=>Ve(e.key)));y.signal.addEventListener("abort",F);let{results:Q,loaderResults:H,fetcherResults:U}=await he(E.matches,P,_,R,w);if(y.signal.aborted)return;y.signal.removeEventListener("abort",F),q.delete(e),k.delete(e),R.forEach((e=>k.delete(e.key)));let W=de(Q);if(W)return se(E,W);let{loaderData:J,errors:$}=ae(E,E.matches,_,H,void 0,R,U,B),G={state:"idle",data:b.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(e,G);let K=Te(V);"loading"===E.navigation.state&&V>L?(s(O,"Expected pending action"),S&&S.abort(),te(E.navigation.location,{matches:P,loaderData:J,errors:$,fetchers:new Map(E.fetchers)})):(z(o({errors:$,loaderData:le(E.loaderData,J,P,$)},K?{fetchers:new Map(E.fetchers)}:{})),I=!1)}(e,a,y,b,m,v):(A.set(e,{routeId:a,path:y}),async function(e,r,a,l,u,p){let d=E.fetchers.get(e),h=o({state:"loading",formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0},p,{data:d&&d.data," _hasFetcherDoneAnything ":!0});E.fetchers.set(e,h),z({fetchers:new Map(E.fetchers)});let f=new AbortController,g=ie(t.history,a,f.signal);k.set(e,f);let m=await oe("loader",g,l,u,i,n,c);if(fe(m)&&(m=await be(m,g.signal,!0)||m),k.get(e)===f&&k.delete(e),g.signal.aborted)return;if(me(m))return N.add(e),void await se(E,m);if(ge(m)){let t=ue(E.matches,r);return E.fetchers.delete(e),void z({fetchers:new Map(E.fetchers),errors:{[t.route.id]:m.error}})}s(!fe(m),"Unhandled fetcher deferred data");let y={state:"idle",data:m.data,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0," _hasFetcherDoneAnything ":!0};E.fetchers.set(e,y),z({fetchers:new Map(E.fetchers)})}(e,a,y,b,m,v))},revalidate:function(){Ce(),z({revalidation:"loading"}),"submitting"!==E.navigation.state&&("idle"!==E.navigation.state?ne(O||E.historyAction,E.navigation.location,{overrideNavigation:E.navigation}):ne(E.historyAction,E.location,{startUninterruptedRevalidation:!0}))},createHref:e=>t.history.createHref(e),encodeLocation:e=>t.history.encodeLocation(e),getFetcher:re,deleteFetcher:Se,dispose:function(){h&&h(),m.clear(),S&&S.abort(),E.fetchers.forEach(((e,t)=>Se(t))),E.blockers.forEach(((e,t)=>_e(t)))},getBlocker:function(e,t){let n=E.blockers.get(e)||J;return F.get(e)!==t&&F.set(e,t),n},deleteBlocker:_e,_internalFetchControllers:k,_internalActiveDeferreds:B,_internalSetRoutes:function(e){i={},r=f(e,n,void 0,i)}},P}({basename:void 0,future:Ke({},void 0,{v7_prependBasename:!0}),history:function(t){return void 0===t&&(t={}),function(t,n,r,a){void 0===a&&(a={});let{window:p=document.defaultView,v5Compat:d=!1}=a,h=p.history,f=e.Pop,g=null,m=y();function y(){return(h.state||{idx:null}).idx}function v(){f=e.Pop;let t=y(),n=null==t?null:t-m;m=t,g&&g({action:f,location:C.location,delta:n})}function b(e){let t="null"!==p.location.origin?p.location.origin:p.location.href,n="string"==typeof e?e:c(e);return s(t,"No window.location.(origin|href) available to create URL for href: "+n),new URL(n,t)}null==m&&(m=0,h.replaceState(o({},h.state,{idx:m}),""));let C={get action(){return f},get location(){return t(p,h)},listen(e){if(g)throw new Error("A history only accepts one active listener");return p.addEventListener(i,v),g=e,()=>{p.removeEventListener(i,v),g=null}},createHref:e=>n(p,e),createURL:b,encodeLocation(e){let t=b(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:function(t,n){f=e.Push;let o=u(C.location,t,n);r&&r(o,t),m=y()+1;let i=l(o,m),s=C.createHref(o);try{h.pushState(i,"",s)}catch(e){p.location.assign(s)}d&&g&&g({action:f,location:C.location,delta:1})},replace:function(t,n){f=e.Replace;let o=u(C.location,t,n);r&&r(o,t),m=y();let i=l(o,m),s=C.createHref(o);h.replaceState(i,"",s),d&&g&&g({action:f,location:C.location,delta:0})},go:e=>h.go(e)};return C}((function(e,t){let{pathname:n,search:r,hash:o}=e.location;return u("",{pathname:n,search:r,hash:o},t.state&&t.state.usr||null,t.state&&t.state.key||"default")}),(function(e,t){return"string"==typeof t?t:c(t)}),null,t)}({window:void 0}),hydrationData:function(){var e;let t=null==(e=window)?void 0:e.__staticRouterHydrationData;return t&&t.errors&&(t=Ke({},t,{errors:Ye(t.errors)})),t}(),routes:vo,mapRouteProperties:function(e){let n={hasErrorBoundary:null!=e.ErrorBoundary||null!=e.errorElement};return e.Component&&Object.assign(n,{element:t.createElement(e.Component),Component:void 0}),e.ErrorBoundary&&Object.assign(n,{errorElement:t.createElement(e.ErrorBoundary),ErrorBoundary:void 0}),n}}).initialize());const Co=function(){return t.createElement("div",{className:"app"},t.createElement(Hr,null,t.createElement(mo,null),t.createElement(We,{router:bo})))};var wo=document.getElementById("root");(0,r.s)(wo).render(t.createElement(t.StrictMode,null,t.createElement(Co,null)))})()})(); \ No newline at end of file diff --git a/setup.py b/setup.py index 0907f75312a07d24e728019013af939725856f6a..f8258a33a0f4a3a9a4cefdd1001fb1e7cf9ee34a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='compendium-v2', - version="0.36", + version="0.37", author='GEANT', author_email='swd@geant.org', description='Flask and React project for displaying ' @@ -29,8 +29,8 @@ setup( include_package_data=True, entry_points={ 'console_scripts': [ - 'survey-publisher-v1=compendium_v2.publishers.survey_publisher_v1:cli', # noqa - 'survey-publisher-2022=compendium_v2.publishers.survey_publisher_2022:cli', # noqa + 'excel-survey-publisher=compendium_v2.publishers.survey_publisher_legacy_excel:cli', # noqa + 'db-publisher-2022=compendium_v2.publishers.survey_publisher_old_db_2022:cli', # noqa 'conversion=compendium_v2.conversion.conversion:cli', # noqa 'dump_survey_model=compendium_v2.migrations.dump_survey_model:cli', # noqa ] diff --git a/survey-frontend/src/UserManagementComponent.tsx b/survey-frontend/src/UserManagementComponent.tsx index d3ea652c2e370225f4f246c667b9cb6e39166845..500209161e6bb59bd87b84ebbc0503cd36b12b8f 100644 --- a/survey-frontend/src/UserManagementComponent.tsx +++ b/survey-frontend/src/UserManagementComponent.tsx @@ -72,13 +72,13 @@ function UserManagementComponent() { const [users, setUsers] = useState<User[]>([]); const [nrens, setNrens] = useState<Nren[]>([]); const { user: loggedInUser } = useContext(userContext); - const [sortFunc, setSortFunc] = useState({ idx: -1, func: defaultSortFunction, asc: true }); + const [sortColumn, setSortColumn] = useState({ idx: -1, asc: true }); const [sortedUsers, setSortedUsers] = useState<User[]>([]); useEffect(() => { fetchUsers().then((userList) => { setUsers(userList); - setSortedUsers(userList.sort(sortFunc.func)) + setSortedUsers(userList.sort(defaultSortFunction)) }); fetchNrens().then((nrenList) => { @@ -115,75 +115,75 @@ function UserManagementComponent() { } const setSort = (index) => { - if (index === sortFunc.idx || ((index === 5 || index === 0) && sortFunc.idx === -1)) { + let func; + if (index === sortColumn.idx || ((index === 5 || index === 0) && sortColumn.idx === -1)) { // reverse sort if (index === 5 || index === 0) { // hack not to show the sort icon on column 0 and 5 index = -1 } - setSortFunc({ idx: index, asc: !sortFunc.asc, func: (a, b) => sortFunc.func(b, a) }) + setSortColumn({ idx: index, asc: !sortColumn.asc }) + setSortedUsers([...sortedUsers.reverse()]) return } + if (index === 0) { // user ID is a UUID which is meaningless to sort by, so use default sort function - setSortFunc({ idx: -1, asc: true, func: defaultSortFunction }) + func = defaultSortFunction + setSortColumn({ idx: -1, asc: true }) } else if (index === 1) { // sort by active - setSortFunc({ - idx: index, asc: true, func: (a, b) => { - if (a.permissions.active && !b.permissions.active) { - return -1; - } else if (!a.permissions.active && b.permissions.active) { - return 1; - } else { - return 0; - } + func = (a, b) => { + if (a.permissions.active && !b.permissions.active) { + return -1; + } else if (!a.permissions.active && b.permissions.active) { + return 1; + } else { + return 0; } - }) + } + setSortColumn({ idx: index, asc: true }) } else if (index === 2) { // sort by role - setSortFunc({ - idx: index, asc: true, func: (a, b) => { - return a.role.localeCompare(b.role) - } - }) + func = (a, b) => { + return a.role.localeCompare(b.role) + } + setSortColumn({ idx: index, asc: true }) } else if (index === 3) { // sort by email - setSortFunc({ - idx: index, asc: true, func: (a, b) => { - return a.email.localeCompare(b.email) - } - }) + func = (a, b) => { + return a.email.localeCompare(b.email) + } + setSortColumn({ idx: index, asc: true }) } else if (index === 4) { // sort by name - setSortFunc({ - idx: index, asc: true, func: (a, b) => { - return a.name.localeCompare(b.name) - } - }) + func = (a, b) => { + return a.name.localeCompare(b.name) + } + setSortColumn({ idx: index, asc: true }) } else if (index === 5) { // use the default sort function, OIDC sub has no meaning when sorting - setSortFunc({ idx: -1, asc: true, func: defaultSortFunction }) + func = defaultSortFunction + setSortColumn({ idx: -1, asc: true }) } else if (index === 6) { // sort by NREN - setSortFunc({ - idx: index, asc: true, func: (a, b) => { - if (a.nrens.length === 0 && b.nrens.length === 0) { - return 0; - } else if (a.nrens.length === 0) { - return -1; - } else if (b.nrens.length === 0) { - return 1; - } else { - return a.nrens[0].localeCompare(b.nrens[0]) - } + func = (a, b) => { + if (a.nrens.length === 0 && b.nrens.length === 0) { + return 0; + } else if (a.nrens.length === 0) { + return -1; + } else if (b.nrens.length === 0) { + return 1; + } else { + return a.nrens[0].localeCompare(b.nrens[0]) } - }) + } + setSortColumn({ idx: index, asc: true }) } else { - setSortFunc({ idx: index, asc: true, func: defaultSortFunction }) + func = defaultSortFunction + setSortColumn({ idx: index, asc: true }) } - - setSortedUsers(users.sort(sortFunc.func)) + setSortedUsers(users.sort(func)) } // build the aria-sort attribute for each column with spreadable objects.. @@ -191,7 +191,7 @@ function UserManagementComponent() { const ariaSort = {} for (let i = 0; i <= 6; i++) { - ariaSort[i] = sortFunc.idx === i ? ({ 'aria-sort': sortFunc.asc ? 'ascending' : 'descending' }) : null + ariaSort[i] = sortColumn.idx === i ? ({ 'aria-sort': sortColumn.asc ? 'ascending' : 'descending' }) : null } return ( diff --git a/test/conftest.py b/test/conftest.py index 2d8b1abb9c2695a390b0720920c6a3627f1aa925..591fe482257bc5bf6e96244309c9f7ed484dc57f 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,7 +6,7 @@ import random from sqlalchemy import select from flask_login import LoginManager # type: ignore import compendium_v2 -from compendium_v2.db import db, model, survey_model +from compendium_v2.db import db, presentation_models, survey_models from compendium_v2.survey_db import model as survey_db_model from compendium_v2.auth.session_management import setup_login_manager, User, ROLES @@ -30,7 +30,7 @@ def mocked_admin_user(app, test_survey_data, mocker): with app.app_context(): user = User(email='testemail123@email.local', fullname='testfullname', oidc_sub='fakesub', roles=ROLES.admin) - nren2 = db.session.scalar(select(model.NREN).filter(model.NREN.name == 'nren2')) + nren2 = db.session.scalar(select(presentation_models.NREN).filter(presentation_models.NREN.name == 'nren2')) user.nrens.append(nren2) db.session.add(user) db.session.commit() @@ -46,7 +46,7 @@ def mocked_user(app, test_survey_data, mocker): with app.app_context(): user = User(email='testemail123@email.local', fullname='testfullname', oidc_sub='fakesub') - nren2 = db.session.scalar(select(model.NREN).filter(model.NREN.name == 'nren2')) + nren2 = db.session.scalar(select(presentation_models.NREN).filter(presentation_models.NREN.name == 'nren2')) user.nrens.append(nren2) db.session.add(user) db.session.commit() @@ -75,7 +75,7 @@ def test_budget_data(app): with app.app_context(): data = [row for row in _test_data_csv("BudgetTestData.csv")] nren_names = set([row["nren"] for row in data]) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for row in data: @@ -83,7 +83,7 @@ def test_budget_data(app): budget = row["budget"] year = row["year"] - db.session.add(model.BudgetEntry(nren=nren, budget=float(budget), year=int(year))) + db.session.add(presentation_models.BudgetEntry(nren=nren, budget=float(budget), year=int(year))) db.session.commit() @@ -92,7 +92,7 @@ def test_funding_source_data(app): with app.app_context(): data = [row for row in _test_data_csv("FundingSourceTestData.csv")] nren_names = set([row["nren"] for row in data]) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for row in data: @@ -105,7 +105,7 @@ def test_funding_source_data(app): other = row["other"] db.session.add( - model.FundingSource( + presentation_models.FundingSource( nren=nren, year=year, client_institutions=client, european_funding=european, @@ -137,7 +137,7 @@ def test_staff_data(app): data = list(_generate_rows()) nren_names = set(d['nren'] for d in data) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for row in data: @@ -149,7 +149,7 @@ def test_staff_data(app): non_technical_fte = row["non_technical_fte"] db.session.add( - model.NrenStaff( + presentation_models.NrenStaff( nren=nren, year=year, permanent_fte=permanent_fte, @@ -165,23 +165,23 @@ def test_staff_data(app): def test_survey_data(app): with app.app_context(): nren_names = ['nren1', 'nren2', 'nren3', 'nren4'] - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) - survey2021 = survey_model.Survey(year=2021, survey={}, status=survey_model.SurveyStatus.published) - survey2022 = survey_model.Survey(year=2022, survey={}, status=survey_model.SurveyStatus.published) - survey2023 = survey_model.Survey( + survey2021 = survey_models.Survey(year=2021, survey={}, status=survey_models.SurveyStatus.published) + survey2022 = survey_models.Survey(year=2022, survey={}, status=survey_models.SurveyStatus.published) + survey2023 = survey_models.Survey( year=2023, survey={'part1': [{'title': 'ha', 'visibleIf': 'false'}]}, - status=survey_model.SurveyStatus.open + status=survey_models.SurveyStatus.open ) db.session.add_all([survey2021, survey2022, survey2023]) - db.session.add(survey_model.SurveyResponse( + db.session.add(survey_models.SurveyResponse( nren=nren_dict['nren1'], survey=survey2023, answers={}, - status=survey_model.ResponseStatus.completed + status=survey_models.ResponseStatus.completed )) db.session.commit() @@ -219,7 +219,7 @@ def test_charging_structure_data(app): with app.app_context(): data = [row for row in _test_data_csv("ChargingStructureTestData.csv")] nren_names = set([row["nren"] for row in data]) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for row in data: @@ -230,7 +230,7 @@ def test_charging_structure_data(app): fee_type = None db.session.add( - model.ChargingStructure( + presentation_models.ChargingStructure( nren=nren, year=year, fee_type=fee_type) ) @@ -264,7 +264,7 @@ def test_organization_data(app): sub_org_data = list(_generate_sub_org_data()) nren_names = set(d['nren'] for d in [*org_data, *sub_org_data]) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for org in org_data: @@ -272,7 +272,7 @@ def test_organization_data(app): year = org["year"] name = org["name"] - db.session.add(model.ParentOrganization(nren=nren, year=year, organization=name)) + db.session.add(presentation_models.ParentOrganization(nren=nren, year=year, organization=name)) for sub_org in sub_org_data: nren = nren_dict[sub_org["nren"]] @@ -280,7 +280,7 @@ def test_organization_data(app): name = sub_org["name"] role = sub_org["role"] - db.session.add(model.SubOrganization(nren=nren, year=year, organization=name, role=role)) + db.session.add(presentation_models.SubOrganization(nren=nren, year=year, organization=name, role=role)) db.session.commit() @@ -307,7 +307,7 @@ def test_ec_project_data(app): ec_project_data = list(_generate_ec_project_data()) nren_names = set(d['nren'] for d in ec_project_data) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for ec_project in ec_project_data: @@ -315,7 +315,7 @@ def test_ec_project_data(app): year = ec_project["year"] project = ec_project["project"] - db.session.add(model.ECProject(nren=nren, year=year, project=project)) + db.session.add(presentation_models.ECProject(nren=nren, year=year, project=project)) db.session.commit() @@ -326,13 +326,13 @@ def test_policy_data(app): nrens_and_years = [('nren1', 2019), ('nren1', 2020), ('nren1', 2021), ('nren2', 2019), ('nren2', 2021)] nren_names = set(ny[0] for ny in nrens_and_years) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for (nren_name, year) in nrens_and_years: nren = nren_dict[nren_name] - db.session.add(model.Policy( + db.session.add(presentation_models.Policy( nren=nren, year=year, strategic_plan='a strategy', @@ -353,14 +353,14 @@ def test_traffic_data(app): with app.app_context(): nrens_and_years = [('nren1', 2019), ('nren1', 2020), ('nren1', 2021), ('nren2', 2019), ('nren2', 2021)] nren_names = set(ny[0] for ny in nrens_and_years) - nren_dict = {nren_name: model.NREN(name=nren_name, country='country') for nren_name in nren_names} + nren_dict = {nren_name: presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names} db.session.add_all(nren_dict.values()) for (nren_name, year) in nrens_and_years: nren = nren_dict[nren_name] db.session.add( - model.TrafficVolume( + presentation_models.TrafficVolume( nren=nren, year=year, from_customers=2.23, @@ -377,7 +377,7 @@ def test_institution_urls_data(app): def _create_and_save_nrens(nren_names): nrens = {} for nren_name in nren_names: - nren_instance = model.NREN(name=nren_name, country='country') + nren_instance = presentation_models.NREN(name=nren_name, country='country') nrens[nren_name] = nren_instance db.session.add(nren_instance) return nrens @@ -387,7 +387,7 @@ def test_institution_urls_data(app): nren_instance = nrens[nren_name] urls = ['https://example.com', 'http://example.org'] - institution_urls_model = model.InstitutionURLs( + institution_urls_model = presentation_models.InstitutionURLs( nren=nren_instance, year=year, urls=urls diff --git a/test/test_budget.py b/test/test_budget.py index 77ea3452c64da2bdaa766de0cd89465630eb3d7b..de0206bcfa0504ce1ba07ca2dba7e389ef9b49c9 100644 --- a/test/test_budget.py +++ b/test/test_budget.py @@ -1,7 +1,7 @@ import json import jsonschema from compendium_v2 import db -from compendium_v2.db.model import PreviewYear +from compendium_v2.db.presentation_models import PreviewYear from compendium_v2.routes.budget import BUDGET_RESPONSE_SCHEMA diff --git a/test/test_conversion.py b/test/test_conversion.py index 0f48050554fadf9b9df037cee1d5e76dfc809d89..d0e2282f4eba63dc1b06274a6cb535522c5a541b 100644 --- a/test/test_conversion.py +++ b/test/test_conversion.py @@ -2,8 +2,8 @@ from sqlalchemy import select from sqlalchemy.orm import lazyload from compendium_v2.db import db -from compendium_v2.db.model import NREN -from compendium_v2.db.survey_model import Survey, SurveyResponse, SurveyStatus +from compendium_v2.db.presentation_models import NREN +from compendium_v2.db.survey_models import Survey, SurveyResponse, SurveyStatus from compendium_v2.conversion.conversion import _cli, convert_answers, load_service_data diff --git a/test/test_survey_publisher_2022.py b/test/test_db_survey_publisher_2022.py similarity index 85% rename from test/test_survey_publisher_2022.py rename to test/test_db_survey_publisher_2022.py index 8f6d1616ae8a9641a1fb077f095424c8668d36f0..bcec6c9804a749219bb9abd36ec023c6e252ce93 100644 --- a/test/test_survey_publisher_2022.py +++ b/test/test_db_survey_publisher_2022.py @@ -1,7 +1,7 @@ from sqlalchemy import select -from compendium_v2.db import db, model, model_enums -from compendium_v2.publishers.survey_publisher_2022 import _cli, FundingSource, \ +from compendium_v2.db import db, presentation_models, model_enums +from compendium_v2.publishers.survey_publisher_old_db_2022 import _cli, FundingSource, \ StaffQuestion, OrgQuestion, ChargingStructure, ECQuestion @@ -200,30 +200,31 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): (163286, 'ANA', 2014, "http://www.rash.al/index.php/network/points-of-presence-pop"), ] - mocker.patch('compendium_v2.publishers.survey_publisher_2022.query_budget', get_rows_as_tuples) - mocker.patch('compendium_v2.publishers.survey_publisher_2022.query_funding_sources', funding_source_data) - mocker.patch('compendium_v2.publishers.survey_publisher_2022.query_question', question_data) - mocker.patch('compendium_v2.publishers.survey_publisher_2022.query_question_id', question_id_data) - mocker.patch('compendium_v2.publishers.survey_publisher_2022.query_institutions_urls', institutions_urls_data) + mocker.patch('compendium_v2.publishers.survey_publisher_old_db_2022.query_budget', get_rows_as_tuples) + mocker.patch('compendium_v2.publishers.survey_publisher_old_db_2022.query_funding_sources', funding_source_data) + mocker.patch('compendium_v2.publishers.survey_publisher_old_db_2022.query_question', question_data) + mocker.patch('compendium_v2.publishers.survey_publisher_old_db_2022.query_question_id', question_id_data) + mocker.patch('compendium_v2.publishers.survey_publisher_old_db_2022.query_institutions_urls', + institutions_urls_data) nren_names = ['Nren1', 'Nren2', 'Nren3', 'Nren4', 'SURF', 'KIFU', 'University of Malta', 'ASNET-AM', 'SIKT', 'LAT', 'RASH', 'ANAS', 'GRNET', 'CSC'] with app_with_survey_db.app_context(): - db.session.add_all([model.NREN(name=nren_name, country='country') for nren_name in nren_names]) + db.session.add_all([presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names]) db.session.commit() _cli(dummy_config, app_with_survey_db) with app_with_survey_db.app_context(): budgets = db.session.scalars( - select(model.BudgetEntry).order_by(model.BudgetEntry.nren_id.asc()) + select(presentation_models.BudgetEntry).order_by(presentation_models.BudgetEntry.nren_id.asc()) ).all() assert len(budgets) == 3 assert budgets[0].nren.name.lower() == 'nren1' assert budgets[0].budget == 100 funding_sources = db.session.scalars( - select(model.FundingSource).order_by(model.FundingSource.nren_id.asc()) + select(presentation_models.FundingSource).order_by(presentation_models.FundingSource.nren_id.asc()) ).all() assert len(funding_sources) == 3 assert funding_sources[0].nren.name.lower() == 'nren1' @@ -242,7 +243,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert funding_sources[2].other == 30 staff_data = db.session.scalars( - select(model.NrenStaff).order_by(model.NrenStaff.nren_id.asc()) + select(presentation_models.NrenStaff).order_by(presentation_models.NrenStaff.nren_id.asc()) ).all() assert len(staff_data) == 3 @@ -265,7 +266,8 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert staff_data[2].subcontracted_fte == 0 _org_data = db.session.scalars( - select(model.ParentOrganization).order_by(model.ParentOrganization.nren_id.asc()) + select(presentation_models.ParentOrganization).order_by( + presentation_models.ParentOrganization.nren_id.asc()) ).all() assert len(_org_data) == 2 @@ -276,7 +278,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert _org_data[1].organization == 'Org3' charging_structures = db.session.scalars( - select(model.ChargingStructure).order_by(model.ChargingStructure.nren_id.asc()) + select(presentation_models.ChargingStructure).order_by(presentation_models.ChargingStructure.nren_id.asc()) ).all() assert len(charging_structures) == 3 assert charging_structures[0].nren.name.lower() == 'nren1' @@ -287,7 +289,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert charging_structures[2].fee_type == model_enums.FeeType.other _ec_data = db.session.scalars( - select(model.ECProject).order_by(model.ECProject.nren_id.asc()) + select(presentation_models.ECProject).order_by(presentation_models.ECProject.nren_id.asc()) ).all() assert len(_ec_data) == 3 @@ -301,7 +303,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert _ec_data[2].project == 'project3' policy_data = db.session.scalars( - select(model.Policy).order_by(model.Policy.nren_id.asc()) + select(presentation_models.Policy).order_by(presentation_models.Policy.nren_id.asc()) ).all() policy_data_2020 = [p for p in policy_data if p.year == 2020] policy_data_2022 = [p for p in policy_data if p.year == 2022] @@ -311,7 +313,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert policy_data_2020[1].strategic_plan == 'policyemail@nren.com' _institution_urls_data = db.session.scalars( - select(model.InstitutionURLs).order_by(model.InstitutionURLs.nren_id.asc()) + select(presentation_models.InstitutionURLs).order_by(presentation_models.InstitutionURLs.nren_id.asc()) ).all() assert len(_institution_urls_data) == 2 assert _institution_urls_data[0].nren.name.lower() == 'rash' diff --git a/test/test_dump_survey_model.py b/test/test_dump_survey_model.py index 722f21dc178f9396f8931130f03a3c6ab71df719..6c49c6fbcff0073aad3000ceaa30ebe9f94113f1 100644 --- a/test/test_dump_survey_model.py +++ b/test/test_dump_survey_model.py @@ -1,5 +1,5 @@ from compendium_v2.db import db -from compendium_v2.db.survey_model import Survey, SurveyStatus +from compendium_v2.db.survey_models import Survey, SurveyStatus from compendium_v2.migrations.dump_survey_model import _cli diff --git a/test/test_response.py b/test/test_response.py index 8d870bb2b0cef3036799497ce9e4ee7128901abe..917ec05187bb3675c9bf4be58324441ccb404b96 100644 --- a/test/test_response.py +++ b/test/test_response.py @@ -1,7 +1,7 @@ import json import jsonschema from compendium_v2.db.auth_model import User -from compendium_v2.db.survey_model import ResponseStatus +from compendium_v2.db.survey_models import ResponseStatus from compendium_v2.routes.response import SURVEY_RESPONSE_SCHEMA, VerificationStatus, SURVEY_LOCK_SCHEMA, \ SURVEY_STATUS_RESPONSE_SCHEMA diff --git a/test/test_survey.py b/test/test_survey.py index dabf95cbb050ec52e7ab5e374f35902b0e93872d..091441279eee0839ddd8fdfab2cb785a5e43f081 100644 --- a/test/test_survey.py +++ b/test/test_survey.py @@ -1,7 +1,7 @@ import json import jsonschema from compendium_v2.db import db -from compendium_v2.db.survey_model import Survey, SurveyStatus +from compendium_v2.db.survey_models import Survey, SurveyStatus from compendium_v2.routes.survey import LIST_SURVEYS_RESPONSE_SCHEMA diff --git a/test/test_survey_publisher_v2.py b/test/test_survey_publisher.py similarity index 71% rename from test/test_survey_publisher_v2.py rename to test/test_survey_publisher.py index d0a0a8cb86a7fd554fcf5b317608ee578c4d1001..b1ca453411e781526f98dfef4270f459015bd463 100644 --- a/test/test_survey_publisher_v2.py +++ b/test/test_survey_publisher.py @@ -5,8 +5,8 @@ import os from sqlalchemy import func, select from compendium_v2 import db -from compendium_v2.db import model, model_enums -from compendium_v2.publishers.survey_publisher_v2 import map_2023 +from compendium_v2.db import presentation_models, model_enums +from compendium_v2.publishers.survey_publisher import _map_2023 JSON_FILE = os.path.join(os.path.dirname(__file__), "data", "2023_all_questions_answered.json") @@ -16,15 +16,15 @@ def test_v2_publisher_empty(app): data = {} with app.app_context(): - nren = model.NREN(name='name', country='country') + nren = presentation_models.NREN(name='name', country='country') db.session.commit() with app.app_context(): - map_2023(nren, {"data": data}) + _map_2023(nren, {"data": data}) db.session.commit() with app.app_context(): - budget_count = db.session.scalar(select(func.count(model.BudgetEntry.year))) + budget_count = db.session.scalar(select(func.count(presentation_models.BudgetEntry.year))) assert budget_count == 0 # the main thing is actually that it doesnt crash @@ -34,37 +34,39 @@ def test_v2_publisher_full(app): data = json.load(json_data) with app.app_context(): - nren = model.NREN(name='name', country='country') + nren = presentation_models.NREN(name='name', country='country') db.session.commit() with app.app_context(): - map_2023(nren, {"data": data}) + _map_2023(nren, {"data": data}) db.session.commit() with app.app_context(): - budget = db.session.scalar(select(model.BudgetEntry.budget)) + budget = db.session.scalar(select(presentation_models.BudgetEntry.budget)) assert budget == Decimal("124.76") - funding_source = db.session.scalar(select(model.FundingSource)) + funding_source = db.session.scalar(select(presentation_models.FundingSource)) assert funding_source.client_institutions == Decimal("0") assert funding_source.european_funding == Decimal("20") assert funding_source.gov_public_bodies == Decimal("70") assert funding_source.commercial == Decimal("0") assert funding_source.other == Decimal("10") - charging_structure = db.session.scalar(select(model.ChargingStructure.fee_type)) + charging_structure = db.session.scalar(select(presentation_models.ChargingStructure.fee_type)) assert charging_structure == model_enums.FeeType.usage_based_fee - staff = db.session.scalar(select(model.NrenStaff)) + staff = db.session.scalar(select(presentation_models.NrenStaff)) assert staff.permanent_fte == Decimal("5.6") assert staff.subcontracted_fte == Decimal("56") assert staff.technical_fte == Decimal("2") assert staff.non_technical_fte == Decimal("1") - parent = db.session.scalar(select(model.ParentOrganization.organization)) + parent = db.session.scalar(select(presentation_models.ParentOrganization.organization)) assert parent == "sdtfgd" - subs = db.session.scalars(select(model.SubOrganization).order_by(model.SubOrganization.organization)) + subs = db.session.scalars(select(presentation_models.SubOrganization).order_by( + presentation_models.SubOrganization.organization) + ) subs = [s for s in subs] assert subs[0].organization == "Aaerer" assert subs[0].role == "Treer" @@ -73,13 +75,15 @@ def test_v2_publisher_full(app): assert subs[2].organization == "werser" assert subs[2].role == "" - projects = db.session.scalars(select(model.ECProject.project).order_by(model.ECProject.project)) + projects = db.session.scalars(select(presentation_models.ECProject.project).order_by( + presentation_models.ECProject.project) + ) projects = [p for p in projects] assert projects[0] == "dgdg" assert projects[1] == "rrrrr" assert projects[2] == "st" - policy = db.session.scalar(select(model.Policy)) + policy = db.session.scalar(select(presentation_models.Policy)) assert policy.strategic_plan == "https://serere.com" assert policy.environmental == "http://mren.ac.me/documents/Environmental%20policy%20for%20MREN.pdf" assert policy.equal_opportunity == "" @@ -89,16 +93,16 @@ def test_v2_publisher_full(app): assert policy.data_protection == "" assert policy.gender_equality == "https://www.ucg.ac.me/objava/blog/616808/objava/148423-plan-rodne-ravnopravnosti-univerziteta-crne-gore" # noqa: E501 - traffic = db.session.scalar(select(model.TrafficVolume)) + traffic = db.session.scalar(select(presentation_models.TrafficVolume)) assert traffic.to_customers == Decimal("3") assert traffic.from_customers == Decimal("34") assert traffic.to_external == Decimal("22") assert traffic.from_external == Decimal("3") - client_urls = db.session.scalar(select(model.InstitutionURLs)) + client_urls = db.session.scalar(select(presentation_models.InstitutionURLs)) assert client_urls.urls == ["http://erse.com", "https://wwe.com"] - external_connections = db.session.scalar(select(model.ExternalConnections)) + external_connections = db.session.scalar(select(presentation_models.ExternalConnections)) external_connection_list = external_connections.connections assert len(external_connection_list) == 6 assert external_connection_list[0] == { diff --git a/test/test_survey_publisher_v1.py b/test/test_survey_publisher_legacy_excel.py similarity index 80% rename from test/test_survey_publisher_v1.py rename to test/test_survey_publisher_legacy_excel.py index d3bd42b6631950951a99df6e545d8a94030c91dc..5a20fc063e843f3d930ce463034f5f910762bf5b 100644 --- a/test/test_survey_publisher_v1.py +++ b/test/test_survey_publisher_legacy_excel.py @@ -3,30 +3,32 @@ import os from sqlalchemy import select, func from compendium_v2 import db -from compendium_v2.db import model -from compendium_v2.publishers.survey_publisher_v1 import _cli +from compendium_v2.db import presentation_models +from compendium_v2.publishers.survey_publisher_legacy_excel import _cli EXCEL_FILE = os.path.join(os.path.dirname(__file__), "data", "2021_Organisation_DataSeries.xlsx") def test_publisher(app_with_survey_db, mocker, dummy_config): - mocker.patch('compendium_v2.background_task.parse_excel_data.EXCEL_FILE', EXCEL_FILE) + mocker.patch('compendium_v2.publishers.excel_parser.EXCEL_FILE', EXCEL_FILE) with app_with_survey_db.app_context(): nren_names = ['SURF', 'KIFU', 'University of Malta', 'ASNET-AM', 'SIKT', 'LAT', 'RASH', 'ANAS', 'GRNET', 'CSC'] - db.session.add_all([model.NREN(name=nren_name, country='country') for nren_name in nren_names]) + db.session.add_all([presentation_models.NREN(name=nren_name, country='country') for nren_name in nren_names]) db.session.commit() _cli(dummy_config, app_with_survey_db) with app_with_survey_db.app_context(): - budget_count = db.session.scalar(select(func.count(model.BudgetEntry.year))) + budget_count = db.session.scalar(select(func.count(presentation_models.BudgetEntry.year))) assert budget_count - funding_source_count = db.session.scalar(select(func.count(model.FundingSource.year))) + funding_source_count = db.session.scalar(select(func.count(presentation_models.FundingSource.year))) assert funding_source_count - charging_structure_count = db.session.scalar(select(func.count(model.ChargingStructure.year))) + charging_structure_count = db.session.scalar(select(func.count(presentation_models.ChargingStructure.year))) assert charging_structure_count - staff_data = db.session.scalars(select(model.NrenStaff).order_by(model.NrenStaff.year.asc())).all() + staff_data = db.session.scalars(select(presentation_models.NrenStaff).order_by( + presentation_models.NrenStaff.year.asc()) + ).all() # data should only be saved for the NRENs we have saved in the database staff_data_nrens = set([staff.nren.name for staff in staff_data]) @@ -72,7 +74,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert kifu_data[5].technical_fte == 133 assert kifu_data[5].non_technical_fte == 45 - ecproject_data = db.session.scalars(select(model.ECProject)).all() + ecproject_data = db.session.scalars(select(presentation_models.ECProject)).all() # test a couple of random entries surf2017 = [x for x in ecproject_data if x.nren.name == 'SURF' and x.year == 2017] assert len(surf2017) == 1 @@ -86,7 +88,7 @@ def test_publisher(app_with_survey_db, mocker, dummy_config): assert len(kifu2019) == 4 assert kifu2019[3].project == 'SuperHeroes for Science' - parent_data = db.session.scalars(select(model.ParentOrganization)).all() + parent_data = db.session.scalars(select(presentation_models.ParentOrganization)).all() # test a random entry asnet2021 = [x for x in parent_data if x.nren.name == 'ASNET-AM' and x.year == 2021] assert len(asnet2021) == 1