From 13ce81ecd29d31726121370167ed814bd009f638 Mon Sep 17 00:00:00 2001
From: Bjarke Madsen <bjarke@nordu.net>
Date: Thu, 24 Oct 2024 19:51:39 +0200
Subject: [PATCH] Refactor publisher
---
compendium_v2/db/presentation_models.py | 100 +--
compendium_v2/publishers/survey_publisher.py | 554 +------------
compendium_v2/publishers/utils.py | 34 +
compendium_v2/publishers/year/__init__.py | 0
compendium_v2/publishers/year/map_2023.py | 826 +++++++++++++++++++
compendium_v2/publishers/year/map_2024.py | 0
test/test_survey_publisher.py | 23 +-
7 files changed, 942 insertions(+), 595 deletions(-)
create mode 100644 compendium_v2/publishers/utils.py
create mode 100644 compendium_v2/publishers/year/__init__.py
create mode 100644 compendium_v2/publishers/year/map_2023.py
create mode 100644 compendium_v2/publishers/year/map_2024.py
diff --git a/compendium_v2/db/presentation_models.py b/compendium_v2/db/presentation_models.py
index 5297ed4e..513860e1 100644
--- a/compendium_v2/db/presentation_models.py
+++ b/compendium_v2/db/presentation_models.py
@@ -18,7 +18,6 @@ from compendium_v2.db.presentation_model_enums import CarryMechanism, Commercial
logger = logging.getLogger(__name__)
-
str128 = Annotated[str, 128]
str128_pk = Annotated[str, mapped_column(String(128), primary_key=True)]
str256_pk = Annotated[str, mapped_column(String(256), primary_key=True)]
@@ -48,13 +47,16 @@ RemoteCampus = TypedDict(
# See https://github.com/pallets-eco/flask-sqlalchemy/issues/1140
# mypy: disable-error-code="name-defined"
+class PresentationModel(db.Model):
+ __abstract__ = True
+
-class PreviewYear(db.Model):
+class PreviewYear(PresentationModel):
__tablename__ = 'preview_year'
year: Mapped[int_pk]
-class NREN(db.Model):
+class NREN(PresentationModel):
__tablename__ = 'nren'
id: Mapped[int_pk]
name: Mapped[str128]
@@ -64,7 +66,7 @@ class NREN(db.Model):
return f'<NREN {self.id} | {self.name}>'
-class BudgetEntry(db.Model):
+class BudgetEntry(PresentationModel):
__tablename__ = 'budgets'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -72,7 +74,7 @@ class BudgetEntry(db.Model):
budget: Mapped[Decimal]
-class FundingSource(db.Model):
+class FundingSource(PresentationModel):
__tablename__ = 'funding_source'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -84,7 +86,7 @@ class FundingSource(db.Model):
other: Mapped[Decimal]
-class ChargingStructure(db.Model):
+class ChargingStructure(PresentationModel):
__tablename__ = 'charging_structure'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -92,7 +94,7 @@ class ChargingStructure(db.Model):
fee_type: Mapped[Optional[FeeType]]
-class NrenStaff(db.Model):
+class NrenStaff(PresentationModel):
__tablename__ = 'nren_staff'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -103,7 +105,7 @@ class NrenStaff(db.Model):
non_technical_fte: Mapped[Decimal]
-class ParentOrganization(db.Model):
+class ParentOrganization(PresentationModel):
__tablename__ = 'parent_organization'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -111,7 +113,7 @@ class ParentOrganization(db.Model):
organization: Mapped[str128]
-class SubOrganization(db.Model):
+class SubOrganization(PresentationModel):
__tablename__ = 'sub_organization'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -120,7 +122,7 @@ class SubOrganization(db.Model):
role: Mapped[str128_pk]
-class ECProject(db.Model):
+class ECProject(PresentationModel):
__tablename__ = 'ec_project'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -128,7 +130,7 @@ class ECProject(db.Model):
project: Mapped[str256_pk]
-class Policy(db.Model):
+class Policy(PresentationModel):
__tablename__ = 'policy'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -143,7 +145,7 @@ class Policy(db.Model):
gender_equality: Mapped[str]
-class TrafficVolume(db.Model):
+class TrafficVolume(PresentationModel):
__tablename__ = 'traffic_volume'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -154,7 +156,7 @@ class TrafficVolume(db.Model):
from_external: Mapped[Decimal]
-class InstitutionURLs(db.Model):
+class InstitutionURLs(PresentationModel):
__tablename__ = 'institution_urls'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -162,7 +164,7 @@ class InstitutionURLs(db.Model):
urls: Mapped[json_str_list]
-class CentralProcurement(db.Model):
+class CentralProcurement(PresentationModel):
__tablename__ = 'central_procurement'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -171,7 +173,7 @@ class CentralProcurement(db.Model):
amount: Mapped[Optional[Decimal]]
-class ServiceManagement(db.Model):
+class ServiceManagement(PresentationModel):
__tablename__ = 'service_management'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -180,7 +182,7 @@ class ServiceManagement(db.Model):
service_level_targets: Mapped[Optional[bool]]
-class ServiceUserTypes(db.Model):
+class ServiceUserTypes(PresentationModel):
__tablename__ = 'service_user_types'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -189,7 +191,7 @@ class ServiceUserTypes(db.Model):
service_category: Mapped[ServiceCategory] = mapped_column(primary_key=True)
-class EOSCListings(db.Model):
+class EOSCListings(PresentationModel):
__tablename__ = 'eosc_listings'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -197,7 +199,7 @@ class EOSCListings(db.Model):
service_names: Mapped[json_str_list]
-class Standards(db.Model):
+class Standards(PresentationModel):
__tablename__ = 'standards'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -209,7 +211,7 @@ class Standards(db.Model):
crisis_management_procedure: Mapped[Optional[bool]]
-class CrisisExercises(db.Model):
+class CrisisExercises(PresentationModel):
__tablename__ = 'crisis_exercises'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -217,7 +219,7 @@ class CrisisExercises(db.Model):
exercise_descriptions: Mapped[json_str_list]
-class SecurityControls(db.Model):
+class SecurityControls(PresentationModel):
__tablename__ = 'security_controls'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -225,7 +227,7 @@ class SecurityControls(db.Model):
security_control_descriptions: Mapped[json_str_list]
-class ConnectedProportion(db.Model):
+class ConnectedProportion(PresentationModel):
__tablename__ = 'connected_proportion'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -237,7 +239,7 @@ class ConnectedProportion(db.Model):
users_served: Mapped[Optional[int]]
-class ConnectivityLevel(db.Model):
+class ConnectivityLevel(PresentationModel):
__tablename__ = 'connectivity_level'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -248,7 +250,7 @@ class ConnectivityLevel(db.Model):
highest_speed_proportion: Mapped[Optional[Decimal]]
-class ConnectionCarrier(db.Model):
+class ConnectionCarrier(PresentationModel):
__tablename__ = 'connection_carrier'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -257,7 +259,7 @@ class ConnectionCarrier(db.Model):
carry_mechanism: Mapped[CarryMechanism]
-class ConnectivityLoad(db.Model):
+class ConnectivityLoad(PresentationModel):
__tablename__ = 'connectivity_load'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -269,7 +271,7 @@ class ConnectivityLoad(db.Model):
peak_load_to_institutions: Mapped[Optional[int]]
-class ConnectivityGrowth(db.Model):
+class ConnectivityGrowth(PresentationModel):
__tablename__ = 'connectivity_growth'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -278,7 +280,7 @@ class ConnectivityGrowth(db.Model):
growth: Mapped[Decimal]
-class CommercialConnectivity(db.Model):
+class CommercialConnectivity(PresentationModel):
__tablename__ = 'commercial_connectivity'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -290,7 +292,7 @@ class CommercialConnectivity(db.Model):
university_spin_off: Mapped[Optional[CommercialConnectivityCoverage]]
-class CommercialChargingLevel(db.Model):
+class CommercialChargingLevel(PresentationModel):
__tablename__ = 'commercial_charging_level'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -300,7 +302,7 @@ class CommercialChargingLevel(db.Model):
direct_peering: Mapped[Optional[CommercialCharges]]
-class RemoteCampuses(db.Model):
+class RemoteCampuses(PresentationModel):
__tablename__ = 'remote_campuses'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -309,7 +311,7 @@ class RemoteCampuses(db.Model):
connections: Mapped[List[RemoteCampus]] = mapped_column(JSON)
-class DarkFibreLease(db.Model):
+class DarkFibreLease(PresentationModel):
__tablename__ = 'dark_fibre_lease'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -320,7 +322,7 @@ class DarkFibreLease(db.Model):
iru_duration: Mapped[Optional[Decimal]]
-class DarkFibreInstalled(db.Model):
+class DarkFibreInstalled(PresentationModel):
__tablename__ = 'dark_fibre_installed'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -329,7 +331,7 @@ class DarkFibreInstalled(db.Model):
fibre_length_in_country: Mapped[Optional[int]]
-class FibreLight(db.Model):
+class FibreLight(PresentationModel):
__tablename__ = 'fibre_light'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -337,7 +339,7 @@ class FibreLight(db.Model):
light_description: Mapped[str]
-class NetworkMapUrls(db.Model):
+class NetworkMapUrls(PresentationModel):
__tablename__ = 'network_map_urls'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -345,7 +347,7 @@ class NetworkMapUrls(db.Model):
urls: Mapped[json_str_list]
-class MonitoringTools(db.Model):
+class MonitoringTools(PresentationModel):
__tablename__ = 'monitoring_tools'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -354,7 +356,7 @@ class MonitoringTools(db.Model):
netflow_processing_description: Mapped[str]
-class PassiveMonitoring(db.Model):
+class PassiveMonitoring(PresentationModel):
__tablename__ = 'passive_monitoring'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -363,7 +365,7 @@ class PassiveMonitoring(db.Model):
method: Mapped[Optional[MonitoringMethod]]
-class TrafficStatistics(db.Model):
+class TrafficStatistics(PresentationModel):
__tablename__ = 'traffic_statistics'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -372,7 +374,7 @@ class TrafficStatistics(db.Model):
urls: Mapped[json_str_list]
-class SiemVendors(db.Model):
+class SiemVendors(PresentationModel):
__tablename__ = 'siem_vendors'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -380,7 +382,7 @@ class SiemVendors(db.Model):
vendor_names: Mapped[json_str_list]
-class CertificateProviders(db.Model):
+class CertificateProviders(PresentationModel):
__tablename__ = 'certificate_providers'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -388,7 +390,7 @@ class CertificateProviders(db.Model):
provider_names: Mapped[json_str_list]
-class WeatherMap(db.Model):
+class WeatherMap(PresentationModel):
__tablename__ = 'weather_map'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -397,7 +399,7 @@ class WeatherMap(db.Model):
url: Mapped[str]
-class PertTeam(db.Model):
+class PertTeam(PresentationModel):
__tablename__ = 'pert_team'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -405,7 +407,7 @@ class PertTeam(db.Model):
pert_team: Mapped[YesNoPlanned]
-class AlienWave(db.Model):
+class AlienWave(PresentationModel):
__tablename__ = 'alien_wave'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -415,7 +417,7 @@ class AlienWave(db.Model):
alien_wave_internal: Mapped[Optional[bool]]
-class Capacity(db.Model):
+class Capacity(PresentationModel):
__tablename__ = 'capacity'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -424,7 +426,7 @@ class Capacity(db.Model):
typical_backbone_capacity: Mapped[Optional[Decimal]]
-class ExternalConnections(db.Model):
+class ExternalConnections(PresentationModel):
__tablename__ = 'external_connections'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -432,7 +434,7 @@ class ExternalConnections(db.Model):
connections: Mapped[List[ExternalConnection]] = mapped_column(JSON)
-class NonREPeers(db.Model):
+class NonREPeers(PresentationModel):
__tablename__ = 'non_r_and_e_peers'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -440,7 +442,7 @@ class NonREPeers(db.Model):
nr_of_non_r_and_e_peers: Mapped[int]
-class TrafficRatio(db.Model):
+class TrafficRatio(PresentationModel):
__tablename__ = 'traffic_ratio'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -449,7 +451,7 @@ class TrafficRatio(db.Model):
commodity_percentage: Mapped[Decimal]
-class OpsAutomation(db.Model):
+class OpsAutomation(PresentationModel):
__tablename__ = 'ops_automation'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -458,7 +460,7 @@ class OpsAutomation(db.Model):
ops_automation_specifics: Mapped[str]
-class NetworkFunctionVirtualisation(db.Model):
+class NetworkFunctionVirtualisation(PresentationModel):
__tablename__ = 'network_function_virtualisation'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -467,7 +469,7 @@ class NetworkFunctionVirtualisation(db.Model):
nfv_specifics: Mapped[json_str_list]
-class NetworkAutomation(db.Model):
+class NetworkAutomation(PresentationModel):
__tablename__ = 'network_automation'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
@@ -476,7 +478,7 @@ class NetworkAutomation(db.Model):
network_automation_specifics: Mapped[json_str_list]
-class Service(db.Model):
+class Service(PresentationModel):
__tablename__ = 'service'
name_key: Mapped[str128_pk]
name: Mapped[str128]
@@ -484,7 +486,7 @@ class Service(db.Model):
description: Mapped[str]
-class NRENService(db.Model):
+class NRENService(PresentationModel):
__tablename__ = 'nren_service'
nren_id: Mapped[int_pk_fkNREN]
nren: Mapped[NREN] = relationship(lazy='joined')
diff --git a/compendium_v2/publishers/survey_publisher.py b/compendium_v2/publishers/survey_publisher.py
index a2a0cdff..8acd5d88 100644
--- a/compendium_v2/publishers/survey_publisher.py
+++ b/compendium_v2/publishers/survey_publisher.py
@@ -9,546 +9,27 @@ Usage:
Used in publish_survey API in compendium_v2/routes/survey.py
"""
-from decimal import Decimal
-from typing import List
+from typing import Sequence, Dict, Any
+from sqlalchemy import select, delete
-from sqlalchemy import delete, select
from compendium_v2.db import db
-from compendium_v2.db.presentation_models import BudgetEntry, ChargingStructure, ECProject, ExternalConnections, \
- InstitutionURLs, NrenStaff, ParentOrganization, Policy, SubOrganization, TrafficVolume, ExternalConnection, \
- FundingSource, CentralProcurement, ServiceManagement, ServiceUserTypes, EOSCListings, \
- Standards, CrisisExercises, SecurityControls, ConnectedProportion, ConnectivityLevel, \
- ConnectionCarrier, ConnectivityLoad, ConnectivityGrowth, CommercialConnectivity, \
- CommercialChargingLevel, RemoteCampuses, DarkFibreLease, DarkFibreInstalled, FibreLight, \
- NetworkMapUrls, MonitoringTools, PassiveMonitoring, TrafficStatistics, SiemVendors, \
- CertificateProviders, WeatherMap, PertTeam, AlienWave, Capacity, NonREPeers, TrafficRatio, \
- OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation, NRENService
-from compendium_v2.db.presentation_model_enums import CarryMechanism, CommercialCharges, YesNoPlanned, \
- CommercialConnectivityCoverage, ConnectivityCoverage, FeeType, MonitoringMethod, ServiceCategory, UserCategory
-from compendium_v2.db.survey_models import ResponseStatus, SurveyResponse
-
-
-def int_or_none(answers_dict, key):
- if key in answers_dict:
- value = answers_dict[key]
- if isinstance(value, str):
- value = value.replace(",", ".")
- return int(answers_dict[key])
- return None
-
-
-def decimal_or_none(answers_dict, key):
- if key in answers_dict:
- value = answers_dict[key]
- if isinstance(value, str):
- value = value.replace(",", ".")
- return Decimal(value)
- return None
-
-
-def decimal_or_zero(answers_dict, key):
- value = answers_dict.get(key, 0)
- if isinstance(value, str):
- value = value.replace(",", ".")
- return Decimal(value)
-
-
-def bool_or_none(answer, key=None):
- if key:
- answer = answer.get(key)
- if answer:
- return answer == "Yes"
- return None
-
-
-def _map_2023(nren, answers) -> None:
- year = 2023
-
- for table_class in [BudgetEntry, ChargingStructure, ECProject, ExternalConnections,
- InstitutionURLs, NrenStaff, ParentOrganization, Policy, SubOrganization, TrafficVolume,
- FundingSource, CentralProcurement, ServiceManagement, ServiceUserTypes, EOSCListings,
- Standards, CrisisExercises, SecurityControls, ConnectedProportion, ConnectivityLevel,
- ConnectionCarrier, ConnectivityLoad, ConnectivityGrowth, CommercialConnectivity,
- CommercialChargingLevel, RemoteCampuses, DarkFibreLease, DarkFibreInstalled, FibreLight,
- NetworkMapUrls, MonitoringTools, PassiveMonitoring, TrafficStatistics, SiemVendors,
- CertificateProviders, WeatherMap, PertTeam, AlienWave, Capacity, NonREPeers, TrafficRatio,
- OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation, NRENService]:
- db.session.execute(delete(table_class).where(table_class.year == year, # type: ignore
- table_class.nren_id == nren.id)) # type: ignore
-
- answers = answers["data"]
- budget = answers.get("budget")
- if budget:
- # replace comma with dot for decimal, surveyjs allows commas for decimals (common in EU countries)
- budget = budget.replace(",", ".")
- db.session.add(BudgetEntry(nren_id=nren.id, nren=nren, year=year, budget=Decimal(budget)))
-
- funding_source = answers.get("income_sources")
- if funding_source:
- db.session.add(FundingSource(
- nren_id=nren.id, nren=nren, year=year,
- client_institutions=decimal_or_zero(funding_source, "client_institutions"),
- european_funding=decimal_or_zero(funding_source, "european_funding"),
- gov_public_bodies=decimal_or_zero(funding_source, "gov_public_bodies"),
- commercial=decimal_or_zero(funding_source, "commercial"),
- other=decimal_or_zero(funding_source, "other")
- ))
-
- charging = answers.get("charging_mechanism")
- if charging:
- db.session.add(ChargingStructure(nren_id=nren.id, nren=nren, year=year, fee_type=FeeType[charging]))
-
- staff_roles = answers.get("staff_roles", {})
- staff_employment_type = answers.get("staff_employment_type", {})
- if staff_roles or staff_employment_type:
- db.session.add(NrenStaff(
- nren_id=nren.id, nren=nren, year=year,
- permanent_fte=decimal_or_zero(staff_employment_type, "permanent_fte"),
- subcontracted_fte=decimal_or_zero(staff_employment_type, "subcontracted_fte"),
- technical_fte=decimal_or_zero(staff_roles, "technical_fte"),
- non_technical_fte=decimal_or_zero(staff_roles, "nontechnical_fte")
- ))
-
- has_parent = answers.get("parent_organization") == "Yes"
- parent = answers.get("parent_organization_name")
- if has_parent and parent:
- db.session.add(ParentOrganization(nren_id=nren.id, nren=nren, year=year, organization=parent))
-
- has_subs = answers.get("suborganizations") == 'Yes'
- subs = answers.get("suborganization_details")
- if has_subs and subs:
- for sub in subs:
- role = sub.get("suborganization_role", "")
- if role == "other":
- role = sub.get("suborganization_role-Comment", "")
- db.session.add(SubOrganization(
- nren_id=nren.id, nren=nren, year=year,
- organization=sub.get("suborganization_name"),
- role=role
- ))
- has_ec_projects = answers.get("ec_projects") == "Yes"
- ec_projects = answers.get("ec_project_names")
- if has_ec_projects and ec_projects:
- for ec_project in ec_projects:
- if ec_project:
- db.session.add(
- ECProject(nren_id=nren.id, nren=nren, year=year, project=ec_project.get("ec_project_name"))
- )
-
- strategy = answers.get("corporate_strategy_url", "")
- policies = answers.get("policies", {})
- if strategy or policies:
- db.session.add(Policy(
- nren_id=nren.id, nren=nren, year=year,
- strategic_plan=strategy,
- environmental=policies.get("environmental_policy", {}).get("url", ""),
- equal_opportunity=policies.get("equal_opportunity_policy", {}).get("url", ""),
- connectivity=policies.get("connectivity_policy", {}).get("url", ""),
- acceptable_use=policies.get("acceptable_use_policy", {}).get("url", ""),
- privacy_notice=policies.get("privacy_notice", {}).get("url", ""),
- data_protection=policies.get("data_protection_contact", {}).get("url", ""),
- gender_equality=policies.get("gender_equality_policy", {}).get("url", "")
- ))
-
- traffic_estimate = answers.get("traffic_estimate")
- if traffic_estimate:
- db.session.add(TrafficVolume(
- nren_id=nren.id, nren=nren, year=year,
- to_customers=decimal_or_zero(traffic_estimate, "to_customers"),
- from_customers=decimal_or_zero(traffic_estimate, "from_customers"),
- to_external=decimal_or_zero(traffic_estimate, "to_external"),
- from_external=decimal_or_zero(traffic_estimate, "from_external")
- ))
-
- institution_urls = answers.get("connected_sites_lists")
- if institution_urls:
- urls = [i.get("connected_sites_url", "") for i in institution_urls if i.get("connected_sites_url", "") != ""]
- if urls:
- db.session.add(InstitutionURLs(
- nren_id=nren.id, nren=nren, year=year,
- urls=urls
- ))
-
- central_procurement = answers.get("central_software_procurement") == "Yes"
- if central_procurement:
- central_procurement_amount = decimal_or_none(answers, "central_procurement_amount")
- else:
- central_procurement_amount = None
- db.session.add(CentralProcurement(
- nren_id=nren.id, nren=nren, year=year,
- central_procurement=central_procurement,
- amount=central_procurement_amount
- ))
-
- formal_service_management_framework = answers.get("formal_service_management_framework")
- service_level_targets = answers.get("service_level_targets")
- if formal_service_management_framework or service_level_targets:
- db.session.add(ServiceManagement(
- nren_id=nren.id, nren=nren, year=year,
- service_management_framework=bool_or_none(formal_service_management_framework),
- service_level_targets=bool_or_none(service_level_targets)
- ))
-
- service_type_matrix = answers.get("service_matrix", {})
- for user_type, service_types in service_type_matrix.items():
- user_type = UserCategory[user_type]
- service_types = service_types["service_types"]
- for service_type in service_types:
- service_type = ServiceCategory[service_type]
- db.session.add(ServiceUserTypes(
- nren_id=nren.id, nren=nren, year=year,
- user_category=user_type,
- service_category=service_type
- ))
-
- has_eosc_listings = answers.get("service_portfolio_eosc_portal") == "Yes"
- services_on_eosc_portal_list = answers.get("services_on_eosc_portal_list", [])
- eosc_list = [i.get("service_name") for i in services_on_eosc_portal_list]
- eosc_list = [i for i in eosc_list if i]
- if has_eosc_listings and eosc_list:
- db.session.add(EOSCListings(
- nren_id=nren.id, nren=nren, year=year,
- service_names=eosc_list
- ))
-
- audits = answers.get("audits")
- business_continuity_plans = answers.get("business_continuity_plans")
- crisis_management_procedure = answers.get("crisis_management_procedure")
- if audits or business_continuity_plans or crisis_management_procedure:
- db.session.add(Standards(
- nren_id=nren.id, nren=nren, year=year,
- audits=bool_or_none(audits),
- audit_specifics=answers.get("audit_specifics", ""),
- business_continuity_plans=bool_or_none(business_continuity_plans),
- business_continuity_plans_specifics=answers.get("business_continuity_plans_specifics", ""),
- crisis_management_procedure=bool_or_none(crisis_management_procedure)
- ))
-
- crisis_exercises = answers.get("crisis_exercises")
- if crisis_exercises:
- db.session.add(CrisisExercises(
- nren_id=nren.id, nren=nren, year=year,
- exercise_descriptions=crisis_exercises
- ))
-
- security_controls = answers.get("security_controls")
- if security_controls:
- if "other" in security_controls:
- security_controls.remove("other")
- security_controls.append(answers.get("security_controls-Comment", "other"))
- db.session.add(SecurityControls(
- nren_id=nren.id, nren=nren, year=year,
- security_control_descriptions=security_controls
- ))
- connectivity_proportions = answers.get("connectivity_proportions", {})
- for user_type, connectivity_proportion in connectivity_proportions.items():
- user_type = UserCategory[user_type]
- coverage = connectivity_proportion.get("covered")
- coverage = ConnectivityCoverage[coverage] if coverage else None
- number_connected = int_or_none(connectivity_proportion, "nr_connected")
- market_share = decimal_or_none(connectivity_proportion, "market_share_percentage")
- users_served = int_or_none(connectivity_proportion, "nr_of_users")
- db.session.add(ConnectedProportion(
- nren_id=nren.id, nren=nren, year=year,
- user_category=user_type,
- coverage=coverage,
- number_connected=number_connected,
- market_share=market_share,
- users_served=users_served
- ))
-
- connectivity_levels = answers.get("connectivity_level", {})
- for user_type, connectivity_level in connectivity_levels.items():
- user_type = UserCategory[user_type]
- db.session.add(ConnectivityLevel(
- nren_id=nren.id, nren=nren, year=year,
- user_category=user_type,
- typical_speed=int_or_none(connectivity_level, "typical_speed"),
- highest_speed=int_or_none(connectivity_level, "highest_speed"),
- highest_speed_proportion=decimal_or_none(connectivity_level, "highest_speed_connection_percentage")
- ))
-
- traffic_carriers = answers.get("traffic_carriers", {})
- for user_type, traffic_carrier in traffic_carriers.items():
- user_type = UserCategory[user_type]
- traffic_carrier = traffic_carrier.get("carry_mechanism")
- if traffic_carrier:
- db.session.add(ConnectionCarrier(
- nren_id=nren.id, nren=nren, year=year,
- user_category=user_type,
- carry_mechanism=CarryMechanism[traffic_carrier]
- ))
-
- traffic_loads = answers.get("traffic_load", {})
- for user_type, traffic_load in traffic_loads.items():
- user_type = UserCategory[user_type]
- db.session.add(ConnectivityLoad(
- nren_id=nren.id, nren=nren, year=year,
- user_category=user_type,
- average_load_from_institutions=int_or_none(traffic_load, "average_from_institutions_to_network"),
- average_load_to_institutions=int_or_none(traffic_load, "average_to_institutions_from_network"),
- peak_load_from_institutions=int_or_none(traffic_load, "peak_from_institutions_to_network"),
- peak_load_to_institutions=int_or_none(traffic_load, "peak_to_institutions_from_network")
- ))
-
- traffic_growths = answers.get("traffic_growth", {})
- for user_type, traffic_growth in traffic_growths.items():
- user_type = UserCategory[user_type]
- db.session.add(ConnectivityGrowth(
- nren_id=nren.id, nren=nren, year=year,
- user_category=user_type,
- growth=decimal_or_zero(traffic_growth, "growth_rate")
- ))
-
- commercial_organizations = answers.get("commercial_organizations")
- if commercial_organizations:
- c1 = commercial_organizations.get("commercial_r_e", {}).get("connection")
- c2 = commercial_organizations.get("commercial_general", {}).get("connection")
- c3 = commercial_organizations.get("commercial_collaboration", {}).get("connection")
- c4 = commercial_organizations.get("commercial_service_provider", {}).get("connection")
- c5 = commercial_organizations.get("university_spin_off", {}).get("connection")
- db.session.add(CommercialConnectivity(
- nren_id=nren.id, nren=nren, year=year,
- commercial_r_and_e=CommercialConnectivityCoverage[c1] if c1 else None,
- commercial_general=CommercialConnectivityCoverage[c2] if c2 else None,
- commercial_collaboration=CommercialConnectivityCoverage[c3] if c3 else None,
- commercial_service_provider=CommercialConnectivityCoverage[c4] if c4 else None,
- university_spin_off=CommercialConnectivityCoverage[c5] if c5 else None,
- ))
-
- commercial_charging_levels = answers.get("commercial_charging_levels")
- if commercial_charging_levels:
- c1 = commercial_charging_levels.get("collaboration", {}).get("charging_level")
- c2 = commercial_charging_levels.get("services", {}).get("charging_level")
- c3 = commercial_charging_levels.get("peering", {}).get("charging_level")
- db.session.add(CommercialChargingLevel(
- nren_id=nren.id, nren=nren, year=year,
- collaboration=CommercialCharges[c1] if c1 else None,
- service_supplier=CommercialCharges[c2] if c2 else None,
- direct_peering=CommercialCharges[c3] if c3 else None,
- ))
-
- remote_campuses = answers.get("remote_campuses")
- if remote_campuses:
- remote_campuses = remote_campuses == "Yes"
-
- if remote_campuses:
- remote_campuses_specifics = answers.get("remote_campuses_specifics", [])
- remote_campuses_specifics = [
- {"country": i.get("country", ""), "local_r_and_e_connection": bool_or_none(i, "connected")}
- for i in remote_campuses_specifics
- ]
- else:
- remote_campuses_specifics = []
- db.session.add(RemoteCampuses(
- nren_id=nren.id, nren=nren, year=year,
- remote_campus_connectivity=remote_campuses,
- connections=remote_campuses_specifics
- ))
-
- dark_fibre_lease = answers.get("dark_fibre_lease") == "Yes"
- if dark_fibre_lease:
- db.session.add(DarkFibreLease(
- nren_id=nren.id, nren=nren, year=year,
- iru_or_lease=dark_fibre_lease,
- fibre_length_in_country=int_or_none(answers, "dark_fibre_lease_kilometers_inside_country"),
- fibre_length_outside_country=int_or_none(answers, "dark_fibre_lease_kilometers_outside_country"),
- iru_duration=decimal_or_none(answers, "dark_fibre_lease_duration")
- ))
-
- dark_fibre_nren = answers.get("dark_fibre_nren") == "Yes"
- if dark_fibre_nren:
- db.session.add(DarkFibreInstalled(
- nren_id=nren.id, nren=nren, year=year,
- installed=dark_fibre_nren,
- fibre_length_in_country=int_or_none(answers, "dark_fibre_nren_kilometers_inside_country")
- ))
-
- fibre_light = answers.get("fibre_light")
- if fibre_light:
- if fibre_light == "other":
- fibre_light = answers.get("fibre_light-Comment", "other")
- db.session.add(FibreLight(
- nren_id=nren.id, nren=nren, year=year,
- light_description=fibre_light
- ))
-
- network_map_urls = answers.get("network_map_urls", [])
- urls = [i.get("network_map_url", "") for i in network_map_urls if i.get("network_map_url", "") != ""]
- if urls:
- db.session.add(NetworkMapUrls(
- nren_id=nren.id, nren=nren, year=year,
- urls=urls
- ))
-
- monitoring_tools = answers.get("monitoring_tools", [])
- netflow_vendors = answers.get("netflow_vendors", "")
- if monitoring_tools or netflow_vendors:
- if "other" in monitoring_tools:
- monitoring_tools.remove("other")
- monitoring_tools.append(answers.get("monitoring_tools-Comment", "other"))
- db.session.add(MonitoringTools(
- nren_id=nren.id, nren=nren, year=year,
- tool_descriptions=monitoring_tools,
- netflow_processing_description=netflow_vendors
- ))
-
- passive_monitoring = answers.get("passive_monitoring")
- if passive_monitoring:
- passive_monitoring = passive_monitoring == "Yes"
- passive_monitoring_tech = answers.get("passive_monitoring_tech")
- db.session.add(PassiveMonitoring(
- nren_id=nren.id, nren=nren, year=year,
- monitoring=passive_monitoring,
- method=MonitoringMethod[passive_monitoring_tech] if passive_monitoring_tech else None
- ))
-
- traffic_statistics = answers.get("traffic_statistics")
- if traffic_statistics:
- traffic_statistics = traffic_statistics == "Yes"
- urls = answers.get("traffic_statistics_urls", [])
- urls = [i.get("traffic_statistics_url", "") for i in urls if i.get("traffic_statistics_url")]
- db.session.add(TrafficStatistics(
- nren_id=nren.id, nren=nren, year=year,
- traffic_statistics=traffic_statistics,
- urls=urls
- ))
-
- siem_soc_vendor = answers.get("siem_soc_vendor")
- if siem_soc_vendor:
- if "other" in siem_soc_vendor:
- siem_soc_vendor.remove("other")
- siem_soc_vendor.append(answers.get("siem_soc_vendor-Comment", "other"))
- db.session.add(SiemVendors(
- nren_id=nren.id, nren=nren, year=year,
- vendor_names=siem_soc_vendor
- ))
-
- certificate_service = answers.get("certificate_service")
- if certificate_service:
- if "other" in certificate_service:
- certificate_service.remove("other")
- certificate_service.append(answers.get("certificate_service-Comment", "other"))
- db.session.add(CertificateProviders(
- nren_id=nren.id, nren=nren, year=year,
- provider_names=certificate_service
- ))
-
- network_weather = answers.get("network_weather")
- if network_weather:
- network_weather = network_weather == "Yes"
- db.session.add(WeatherMap(
- nren_id=nren.id, nren=nren, year=year,
- weather_map=network_weather,
- url=answers.get("network_weather_url", "")
- ))
-
- pert_team = answers.get("pert_team")
- if pert_team:
- pert_team = YesNoPlanned[pert_team.lower()]
- db.session.add(PertTeam(
- nren_id=nren.id, nren=nren, year=year,
- pert_team=pert_team
- ))
-
- alienwave_services = answers.get("alienwave_services")
- alienwave_internal = answers.get("alienwave_internal")
- if alienwave_services or alienwave_internal:
- alienwave_services = YesNoPlanned[alienwave_services.lower()] if alienwave_services else None
- alienwave_internal = alienwave_internal == "Yes" if alienwave_internal else None
- db.session.add(AlienWave(
- nren_id=nren.id, nren=nren, year=year,
- alien_wave_third_pary=alienwave_services,
- nr_of_alien_wave_third_party_services=int_or_none(answers, "alienwave_services_number"),
- alien_wave_internal=alienwave_internal
- ))
-
- max_capacity = answers.get("max_capacity")
- typical_capacity = answers.get("typical_capacity")
- if max_capacity or typical_capacity:
- db.session.add(Capacity(
- nren_id=nren.id, nren=nren, year=year,
- largest_link_capacity=decimal_or_none(answers, "max_capacity"),
- typical_backbone_capacity=decimal_or_none(answers, "typical_capacity")
- ))
-
- external_connections = answers.get("external_connections")
- if external_connections:
- connections: List[ExternalConnection] = []
- for connection in external_connections:
- connections.append({
- 'link_name': connection.get('link_name', ''),
- 'capacity': connection.get('capacity'),
- 'from_organization': connection.get('from_organization', ''),
- 'to_organization': connection.get('to_organization', ''),
- 'interconnection_method': connection.get('interconnection_method')
- })
- db.session.add(ExternalConnections(
- nren_id=nren.id, nren=nren, year=year,
- connections=connections
- ))
-
- non_r_and_e_peers = answers.get("non_r_and_e_peers")
- if non_r_and_e_peers:
- db.session.add(NonREPeers(
- nren_id=nren.id, nren=nren, year=year,
- nr_of_non_r_and_e_peers=int(non_r_and_e_peers)
- ))
-
- commodity_vs_r_e = answers.get("commodity_vs_r_e")
- if commodity_vs_r_e:
- db.session.add(TrafficRatio(
- nren_id=nren.id, nren=nren, year=year,
- r_and_e_percentage=decimal_or_zero(commodity_vs_r_e, "r_e"),
- commodity_percentage=decimal_or_zero(commodity_vs_r_e, "commodity"),
- ))
-
- operational_process_automation = answers.get("operational_process_automation")
- if operational_process_automation:
- db.session.add(OpsAutomation(
- nren_id=nren.id, nren=nren, year=year,
- ops_automation=YesNoPlanned[operational_process_automation.lower()],
- ops_automation_specifics=answers.get("operational_process_automation_tools", "")
- ))
-
- nfv = answers.get("nfv")
- if nfv:
- nfv_types = answers.get("nfv_types", [])
- if "other" in nfv_types:
- nfv_types.remove("other")
- nfv_types.append(answers.get("nfv_types-Comment", "other"))
- db.session.add(NetworkFunctionVirtualisation(
- nren_id=nren.id, nren=nren, year=year,
- nfv=YesNoPlanned[nfv.lower()],
- nfv_specifics=nfv_types
- ))
+from compendium_v2.db.survey_models import ResponseStatus, SurveyResponse
+from compendium_v2.publishers.year.map_2023 import map_2023
+from compendium_v2.db.presentation_models import PresentationModel
- network_automation = answers.get("network_automation")
- if network_automation:
- network_automation_tasks = answers.get("network_automation_tasks", [])
- db.session.add(NetworkAutomation(
- nren_id=nren.id, nren=nren, year=year,
- network_automation=YesNoPlanned[network_automation.lower()],
- network_automation_specifics=network_automation_tasks
- ))
- all_services = {}
- for question_name in ["services_collaboration", "services_hosting", "services_identity", "services_isp",
- "services_multimedia", "services_network", "services_professional", "services_security"]:
- all_services.update(answers.get(question_name, {}))
+def save_data(year: int, data: Dict[PresentationModel, Sequence[Dict[str, Any]]]):
+ table_classes = list(data.keys())
+ if not all(issubclass(table, db.Model) for table in table_classes):
+ raise ValueError("All tables must be subclasses of db.Model")
- for service_key, answers in all_services.items():
- offered = answers.get("offered")
- if offered == ["yes"]:
- db.session.add(NRENService(
- nren_id=nren.id, nren=nren, year=year,
- service_key=service_key,
- product_name=answers.get("name", ""),
- additional_information=answers.get("additional_information", ""),
- official_description=answers.get("description", "")
- ))
+ for table_class in table_classes:
+ db.session.execute(delete(table_class).where(table_class.year == year))
+ for model, rows in data.items():
+ db.session.bulk_insert_mappings(model, rows) # type: ignore
+ db.session.commit()
def publish(year):
@@ -558,7 +39,7 @@ def publish(year):
).unique()
question_mapping = {
- 2023: _map_2023
+ 2023: map_2023
}
mapping_function = question_mapping.get(year)
@@ -566,5 +47,6 @@ def publish(year):
if not mapping_function:
raise ValueError(f"No publishing function found for the {year} survey.")
- for response in responses:
- mapping_function(response.nren, response.answers)
+ data = mapping_function(responses)
+
+ save_data(year, data)
diff --git a/compendium_v2/publishers/utils.py b/compendium_v2/publishers/utils.py
new file mode 100644
index 00000000..04b1f66d
--- /dev/null
+++ b/compendium_v2/publishers/utils.py
@@ -0,0 +1,34 @@
+from decimal import Decimal
+
+
+def int_or_none(answers_dict, key):
+ if key in answers_dict:
+ value = answers_dict[key]
+ if isinstance(value, str):
+ value = value.replace(",", ".")
+ return int(answers_dict[key])
+ return None
+
+
+def decimal_or_none(answers_dict, key):
+ if key in answers_dict:
+ value = answers_dict[key]
+ if isinstance(value, str):
+ value = value.replace(",", ".")
+ return Decimal(value)
+ return None
+
+
+def decimal_or_zero(answers_dict, key):
+ value = answers_dict.get(key, 0)
+ if isinstance(value, str):
+ value = value.replace(",", ".")
+ return Decimal(value)
+
+
+def bool_or_none(answer, key=None):
+ if key:
+ answer = answer.get(key)
+ if answer:
+ return answer == "Yes"
+ return None
diff --git a/compendium_v2/publishers/year/__init__.py b/compendium_v2/publishers/year/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/compendium_v2/publishers/year/map_2023.py b/compendium_v2/publishers/year/map_2023.py
new file mode 100644
index 00000000..8a17ef5f
--- /dev/null
+++ b/compendium_v2/publishers/year/map_2023.py
@@ -0,0 +1,826 @@
+from decimal import Decimal
+from typing import List, Dict, Any, Type
+from collections import defaultdict
+from compendium_v2.db.presentation_models import BudgetEntry, ChargingStructure, ECProject, ExternalConnections, \
+ InstitutionURLs, NrenStaff, ParentOrganization, Policy, SubOrganization, TrafficVolume, ExternalConnection, \
+ FundingSource, CentralProcurement, ServiceManagement, ServiceUserTypes, EOSCListings, \
+ Standards, CrisisExercises, SecurityControls, ConnectedProportion, ConnectivityLevel, \
+ ConnectionCarrier, ConnectivityLoad, ConnectivityGrowth, CommercialConnectivity, \
+ CommercialChargingLevel, RemoteCampuses, DarkFibreLease, DarkFibreInstalled, FibreLight, \
+ NetworkMapUrls, MonitoringTools, PassiveMonitoring, TrafficStatistics, SiemVendors, \
+ CertificateProviders, WeatherMap, PertTeam, AlienWave, Capacity, NonREPeers, TrafficRatio, \
+ OpsAutomation, NetworkFunctionVirtualisation, NetworkAutomation, NRENService, NREN, PresentationModel
+from compendium_v2.db.presentation_model_enums import CarryMechanism, CommercialCharges, YesNoPlanned, \
+ CommercialConnectivityCoverage, ConnectivityCoverage, FeeType, MonitoringMethod, ServiceCategory, UserCategory
+from compendium_v2.db.survey_models import SurveyResponse
+from compendium_v2.publishers.utils import decimal_or_zero, decimal_or_none, bool_or_none, int_or_none
+
+
+def map_budget_entry(year, nren, answers):
+ budget = answers.get("budget")
+ if budget:
+ # replace comma with dot for decimal, surveyjs allows commas for decimals (common in EU countries)
+ budget = budget.replace(",", ".")
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'budget': Decimal(budget)
+ }
+
+
+def map_fundingsource_entry(year, nren, answers):
+ funding_source = answers.get("income_sources")
+ if funding_source:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'client_institutions': decimal_or_zero(funding_source, "client_institutions"),
+ 'european_funding': decimal_or_zero(funding_source, "european_funding"),
+ 'gov_public_bodies': decimal_or_zero(funding_source, "gov_public_bodies"),
+ 'commercial': decimal_or_zero(funding_source, "commercial"),
+ 'other': decimal_or_zero(funding_source, "other")
+ }
+
+
+def map_charging_structure_entry(year, nren, answers):
+ charging = answers.get("charging_mechanism")
+ if charging:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'fee_type': FeeType[charging]
+ }
+
+
+def map_nren_staff(year, nren, answers):
+ staff_roles = answers.get("staff_roles", {})
+ staff_employment_type = answers.get("staff_employment_type", {})
+ if staff_roles or staff_employment_type:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'permanent_fte': decimal_or_zero(staff_employment_type, "permanent_fte"),
+ 'subcontracted_fte': decimal_or_zero(staff_employment_type, "subcontracted_fte"),
+ 'technical_fte': decimal_or_zero(staff_roles, "technical_fte"),
+ 'non_technical_fte': decimal_or_zero(staff_roles, "nontechnical_fte")
+ }
+
+
+def map_parent_orgs(nren: NREN, year: int, answers: Dict[str, Any]):
+ has_parent = answers.get("parent_organization") == "Yes"
+ parent = answers.get("parent_organization_name")
+ if has_parent and parent:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'organization': parent
+ }
+
+
+def map_sub_orgs(nren: NREN, year: int, answers: Dict[str, Any]):
+ has_subs = answers.get("suborganizations") == 'Yes'
+ subs = answers.get("suborganization_details")
+ if has_subs and subs:
+ for sub in subs:
+ role = sub.get("suborganization_role", "")
+ if role == "other":
+ role = sub.get("suborganization_role-Comment", "")
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'organization': sub.get("suborganization_name"),
+ 'role': role
+ }
+
+
+def map_ec_projects(nren: NREN, year: int, answers: Dict[str, Any]):
+ has_ec_projects = answers.get("ec_projects") == "Yes"
+ ec_projects = answers.get("ec_project_names")
+ if has_ec_projects and ec_projects:
+ for ec_project in ec_projects:
+ if ec_project:
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'project': ec_project.get("ec_project_name")
+ }
+
+
+def map_policy(nren: NREN, year: int, answers: Dict[str, Any]):
+ strategy = answers.get("corporate_strategy_url", "")
+ policies = answers.get("policies", {})
+ if strategy or policies:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'strategic_plan': strategy,
+ 'environmental': policies.get("environmental_policy", {}).get("url", ""),
+ 'equal_opportunity': policies.get("equal_opportunity_policy", {}).get("url", ""),
+ 'connectivity': policies.get("connectivity_policy", {}).get("url", ""),
+ 'acceptable_use': policies.get("acceptable_use_policy", {}).get("url", ""),
+ 'privacy_notice': policies.get("privacy_notice", {}).get("url", ""),
+ 'data_protection': policies.get("data_protection_contact", {}).get("url", ""),
+ 'gender_equality': policies.get("gender_equality_policy", {}).get("url", "")
+ }
+
+
+def map_traffic_volume(nren: NREN, year: int, answers: Dict[str, Any]):
+ traffic_estimate = answers.get("traffic_estimate")
+ if traffic_estimate:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'to_customers': decimal_or_zero(traffic_estimate, "to_customers"),
+ 'from_customers': decimal_or_zero(traffic_estimate, "from_customers"),
+ 'to_external': decimal_or_zero(traffic_estimate, "to_external"),
+ 'from_external': decimal_or_zero(traffic_estimate, "from_external")
+ }
+
+
+def map_institution_urls(nren: NREN, year: int, answers: Dict[str, Any]):
+ institution_urls = answers.get("connected_sites_lists")
+ if institution_urls:
+ urls = [i.get("connected_sites_url", "")
+ for i in institution_urls if i.get("connected_sites_url", "")]
+ if urls:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'urls': urls
+ }
+
+
+def map_central_procurement(nren: NREN, year: int, answers: Dict[str, Any]):
+ central_procurement = answers.get("central_software_procurement") == "Yes"
+ if central_procurement:
+ central_procurement_amount = decimal_or_none(answers, "central_procurement_amount")
+ else:
+ central_procurement_amount = None
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'central_procurement': central_procurement,
+ 'amount': central_procurement_amount
+ }
+
+
+def map_service_management(nren: NREN, year: int, answers: Dict[str, Any]):
+ formal_service_management_framework = answers.get("formal_service_management_framework")
+ service_level_targets = answers.get("service_level_targets")
+ if formal_service_management_framework or service_level_targets:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'service_management_framework': bool_or_none(formal_service_management_framework),
+ 'service_level_targets': bool_or_none(service_level_targets)
+ }
+
+
+def map_service_user_types(nren: NREN, year: int, answers: Dict[str, Any]):
+ service_type_matrix = answers.get("service_matrix", {})
+ for user_type, service_types in service_type_matrix.items():
+ user_type_enum = UserCategory[user_type]
+ service_types_list = service_types["service_types"]
+ for service_type in service_types_list:
+ service_type_enum = ServiceCategory[service_type]
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'user_category': user_type_enum,
+ 'service_category': service_type_enum
+ }
+
+
+def map_eosc_listings(nren: NREN, year: int, answers: Dict[str, Any]):
+ has_eosc_listings = answers.get("service_portfolio_eosc_portal") == "Yes"
+ services_on_eosc_portal_list = answers.get("services_on_eosc_portal_list", [])
+ eosc_list = [i.get("service_name") for i in services_on_eosc_portal_list if i.get("service_name")]
+ if has_eosc_listings and eosc_list:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'service_names': eosc_list
+ }
+
+
+def map_standards(nren: NREN, year: int, answers: Dict[str, Any]):
+ audits = answers.get("audits")
+ business_continuity_plans = answers.get("business_continuity_plans")
+ crisis_management_procedure = answers.get("crisis_management_procedure")
+ if audits or business_continuity_plans or crisis_management_procedure:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'audits': bool_or_none(audits),
+ 'audit_specifics': answers.get("audit_specifics", ""),
+ 'business_continuity_plans': bool_or_none(business_continuity_plans),
+ 'business_continuity_plans_specifics': answers.get("business_continuity_plans_specifics", ""),
+ 'crisis_management_procedure': bool_or_none(crisis_management_procedure)
+ }
+
+
+def map_crisis_exercises(nren: NREN, year: int, answers: Dict[str, Any]):
+ crisis_exercises = answers.get("crisis_exercises")
+ if crisis_exercises:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'exercise_descriptions': crisis_exercises
+ }
+
+
+def map_security_controls(nren: NREN, year: int, answers: Dict[str, Any]):
+ security_controls = answers.get("security_controls")
+ if security_controls:
+ if "other" in security_controls:
+ security_controls.remove("other")
+ security_controls.append(answers.get("security_controls-Comment", "other"))
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'security_control_descriptions': security_controls
+ }
+
+
+def map_connected_proportion(nren: NREN, year: int, answers: Dict[str, Any]):
+ connectivity_proportions = answers.get("connectivity_proportions", {})
+ for user_type, connectivity_proportion in connectivity_proportions.items():
+ user_type_enum = UserCategory[user_type]
+ coverage = connectivity_proportion.get("covered")
+ coverage_enum = ConnectivityCoverage[coverage] if coverage else None
+ number_connected = int_or_none(connectivity_proportion, "nr_connected")
+ market_share = decimal_or_none(connectivity_proportion, "market_share_percentage")
+ users_served = int_or_none(connectivity_proportion, "nr_of_users")
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'user_category': user_type_enum,
+ 'coverage': coverage_enum,
+ 'number_connected': number_connected,
+ 'market_share': market_share,
+ 'users_served': users_served
+ }
+
+
+def map_connectivity_levels(nren: NREN, year: int, answers: Dict[str, Any]):
+ connectivity_levels = answers.get("connectivity_level", {})
+ for user_type, connectivity_level in connectivity_levels.items():
+ user_type_enum = UserCategory[user_type]
+ typical_speed = int_or_none(connectivity_level, "typical_speed")
+ highest_speed = int_or_none(connectivity_level, "highest_speed")
+ highest_speed_proportion = decimal_or_none(connectivity_level, "highest_speed_connection_percentage")
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'user_category': user_type_enum,
+ 'typical_speed': typical_speed,
+ 'highest_speed': highest_speed,
+ 'highest_speed_proportion': highest_speed_proportion
+ }
+
+
+def map_connection_carriers(nren: NREN, year: int, answers: Dict[str, Any]):
+ traffic_carriers = answers.get("traffic_carriers", {})
+ for user_type, traffic_carrier in traffic_carriers.items():
+ user_type_enum = UserCategory[user_type]
+ carry_mechanism = traffic_carrier.get("carry_mechanism")
+ if carry_mechanism:
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'user_category': user_type_enum,
+ 'carry_mechanism': CarryMechanism[carry_mechanism]
+ }
+
+
+def map_connectivity_loads(nren: NREN, year: int, answers: Dict[str, Any]):
+ traffic_loads = answers.get("traffic_load", {})
+ for user_type, traffic_load in traffic_loads.items():
+ user_type_enum = UserCategory[user_type]
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'user_category': user_type_enum,
+ 'average_load_from_institutions': int_or_none(traffic_load, "average_from_institutions_to_network"),
+ 'average_load_to_institutions': int_or_none(traffic_load, "average_to_institutions_from_network"),
+ 'peak_load_from_institutions': int_or_none(traffic_load, "peak_from_institutions_to_network"),
+ 'peak_load_to_institutions': int_or_none(traffic_load, "peak_to_institutions_from_network")
+ }
+
+
+def map_connectivity_growth(nren: NREN, year: int, answers: Dict[str, Any]):
+ traffic_growths = answers.get("traffic_growth", {})
+ for user_type, traffic_growth in traffic_growths.items():
+ user_type_enum = UserCategory[user_type]
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'user_category': user_type_enum,
+ 'growth': decimal_or_zero(traffic_growth, "growth_rate")
+ }
+
+
+def map_commercial_connectivity(nren: NREN, year: int, answers: Dict[str, Any]):
+ commercial_organizations = answers.get("commercial_organizations")
+ if commercial_organizations:
+ c1 = commercial_organizations.get("commercial_r_e", {}).get("connection")
+ c2 = commercial_organizations.get("commercial_general", {}).get("connection")
+ c3 = commercial_organizations.get("commercial_collaboration", {}).get("connection")
+ c4 = commercial_organizations.get("commercial_service_provider", {}).get("connection")
+ c5 = commercial_organizations.get("university_spin_off", {}).get("connection")
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'commercial_r_and_e': CommercialConnectivityCoverage[c1] if c1 else None,
+ 'commercial_general': CommercialConnectivityCoverage[c2] if c2 else None,
+ 'commercial_collaboration': CommercialConnectivityCoverage[c3] if c3 else None,
+ 'commercial_service_provider': CommercialConnectivityCoverage[c4] if c4 else None,
+ 'university_spin_off': CommercialConnectivityCoverage[c5] if c5 else None,
+ }
+
+
+def map_commercial_charging_levels(nren: NREN, year: int, answers: Dict[str, Any]):
+ commercial_charging_levels = answers.get("commercial_charging_levels")
+ if commercial_charging_levels:
+ c1 = commercial_charging_levels.get("collaboration", {}).get("charging_level")
+ c2 = commercial_charging_levels.get("services", {}).get("charging_level")
+ c3 = commercial_charging_levels.get("peering", {}).get("charging_level")
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'collaboration': CommercialCharges[c1] if c1 else None,
+ 'service_supplier': CommercialCharges[c2] if c2 else None,
+ 'direct_peering': CommercialCharges[c3] if c3 else None,
+ }
+
+
+def map_remote_campuses(nren: NREN, year: int, answers: Dict[str, Any]):
+ remote_campuses = answers.get("remote_campuses")
+ if remote_campuses:
+ remote_campuses = remote_campuses == "Yes"
+ remote_campuses_specifics = []
+ if remote_campuses:
+ remote_campuses_specifics = [
+ {"country": i.get("country", ""), "local_r_and_e_connection": bool_or_none(i, "connected")}
+ for i in answers.get("remote_campuses_specifics", [])
+ ]
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'remote_campus_connectivity': remote_campuses,
+ 'connections': remote_campuses_specifics
+ }
+
+
+def map_dark_fibre_lease(nren: NREN, year: int, answers: Dict[str, Any]):
+ dark_fibre_lease = answers.get("dark_fibre_lease") == "Yes"
+ if dark_fibre_lease:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'iru_or_lease': dark_fibre_lease,
+ 'fibre_length_in_country': int_or_none(answers, "dark_fibre_lease_kilometers_inside_country"),
+ 'fibre_length_outside_country': int_or_none(answers, "dark_fibre_lease_kilometers_outside_country"),
+ 'iru_duration': decimal_or_none(answers, "dark_fibre_lease_duration")
+ }
+
+
+def map_dark_fibre_installed(nren: NREN, year: int, answers: Dict[str, Any]):
+ dark_fibre_nren = answers.get("dark_fibre_nren") == "Yes"
+ if dark_fibre_nren:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'installed': dark_fibre_nren,
+ 'fibre_length_in_country': int_or_none(answers, "dark_fibre_nren_kilometers_inside_country")
+ }
+
+
+def map_fibre_light(nren: NREN, year: int, answers: Dict[str, Any]):
+ fibre_light = answers.get("fibre_light")
+ if fibre_light:
+ if fibre_light == "other":
+ fibre_light = answers.get("fibre_light-Comment", "other")
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'light_description': fibre_light
+ }
+
+
+def map_monitoring_tools(nren: NREN, year: int, answers: Dict[str, Any]):
+ monitoring_tools = answers.get("monitoring_tools", [])
+ netflow_vendors = answers.get("netflow_vendors", "")
+ if monitoring_tools or netflow_vendors:
+ if "other" in monitoring_tools:
+ monitoring_tools.remove("other")
+ monitoring_tools.append(answers.get("monitoring_tools-Comment", "other"))
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'tool_descriptions': monitoring_tools,
+ 'netflow_processing_description': netflow_vendors
+ }
+
+
+def map_passive_monitoring(nren: NREN, year: int, answers: Dict[str, Any]):
+ passive_monitoring = answers.get("passive_monitoring")
+ if passive_monitoring:
+ passive_monitoring = passive_monitoring == "Yes"
+ passive_monitoring_tech = answers.get("passive_monitoring_tech")
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'monitoring': passive_monitoring,
+ 'method': MonitoringMethod[passive_monitoring_tech] if passive_monitoring_tech else None
+ }
+
+
+def map_traffic_statistics(nren: NREN, year: int, answers: Dict[str, Any]):
+ traffic_statistics = answers.get("traffic_statistics")
+ if traffic_statistics:
+ traffic_statistics = traffic_statistics == "Yes"
+ urls = answers.get("traffic_statistics_urls", [])
+ urls = [i.get("traffic_statistics_url", "") for i in urls if i.get("traffic_statistics_url")]
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'traffic_statistics': traffic_statistics,
+ 'urls': urls
+ }
+
+
+def map_siem_vendors(nren: NREN, year: int, answers: Dict[str, Any]):
+ siem_soc_vendor = answers.get("siem_soc_vendor")
+ if siem_soc_vendor:
+ if "other" in siem_soc_vendor:
+ siem_soc_vendor.remove("other")
+ siem_soc_vendor.append(answers.get("siem_soc_vendor-Comment", "other"))
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'vendor_names': siem_soc_vendor
+ }
+
+
+def map_network_map_urls(nren: NREN, year: int, answers: Dict[str, Any]):
+ network_map_urls = answers.get("network_map_urls", [])
+ urls = [i.get("network_map_url", "") for i in network_map_urls if i.get("network_map_url", "")]
+ if urls:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'urls': urls
+ }
+
+
+def map_certificate_providers(nren: NREN, year: int, answers: Dict[str, Any]):
+ certificate_service = answers.get("certificate_service")
+ if certificate_service:
+ if "other" in certificate_service:
+ certificate_service.remove("other")
+ certificate_service.append(answers.get("certificate_service-Comment", "other"))
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'provider_names': certificate_service
+ }
+
+
+def map_weathermap_entry(nren: NREN, year: int, answers: Dict[str, Any]):
+ network_weather = answers.get("network_weather")
+ if network_weather:
+ network_weather = network_weather == "Yes"
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'weather_map': network_weather,
+ 'url': answers.get("network_weather_url", "")
+ }
+
+
+def map_pert_team(nren: NREN, year: int, answers: Dict[str, Any]):
+ pert_team = answers.get("pert_team")
+ if pert_team:
+ pert_team = YesNoPlanned[pert_team.lower()]
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'pert_team': pert_team
+ }
+
+
+def map_alienwave_entry(nren: NREN, year: int, answers: Dict[str, Any]):
+ alienwave_services = answers.get("alienwave_services")
+ alienwave_internal = answers.get("alienwave_internal")
+ if alienwave_services or alienwave_internal:
+ alienwave_services = YesNoPlanned[alienwave_services.lower()] if alienwave_services else None
+ alienwave_internal = alienwave_internal == "Yes" if alienwave_internal else None
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'alien_wave_third_pary': alienwave_services,
+ 'nr_of_alien_wave_third_party_services': int_or_none(answers, "alienwave_services_number"),
+ 'alien_wave_internal': alienwave_internal
+ }
+
+
+def map_capacity_entry(nren: NREN, year: int, answers: Dict[str, Any]):
+ max_capacity = answers.get("max_capacity")
+ typical_capacity = answers.get("typical_capacity")
+ if max_capacity or typical_capacity:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'largest_link_capacity': decimal_or_none(answers, "max_capacity"),
+ 'typical_backbone_capacity': decimal_or_none(answers, "typical_capacity")
+ }
+
+
+def map_external_connections(nren: NREN, year: int, answers: Dict[str, Any]):
+ external_connections = answers.get("external_connections")
+ if external_connections:
+ connections: List[ExternalConnection] = [{
+ 'link_name': connection.get('link_name', ''),
+ 'capacity': connection.get('capacity'),
+ 'from_organization': connection.get('from_organization', ''),
+ 'to_organization': connection.get('to_organization', ''),
+ 'interconnection_method': connection.get('interconnection_method')
+ } for connection in external_connections]
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'connections': connections
+ }
+
+
+def map_non_re_peers(nren: NREN, year: int, answers: Dict[str, Any]):
+ non_r_and_e_peers = answers.get("non_r_and_e_peers")
+ if non_r_and_e_peers:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'nr_of_non_r_and_e_peers': int(non_r_and_e_peers)
+ }
+
+
+def map_traffic_ratio(nren: NREN, year: int, answers: Dict[str, Any]):
+ commodity_vs_r_e = answers.get("commodity_vs_r_e")
+ if commodity_vs_r_e:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'r_and_e_percentage': decimal_or_zero(commodity_vs_r_e, "r_e"),
+ 'commodity_percentage': decimal_or_zero(commodity_vs_r_e, "commodity"),
+ }
+
+
+def map_ops_automation(nren: NREN, year: int, answers: Dict[str, Any]):
+ operational_process_automation = answers.get("operational_process_automation")
+ if operational_process_automation:
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'ops_automation': YesNoPlanned[operational_process_automation.lower()],
+ 'ops_automation_specifics': answers.get("operational_process_automation_tools", "")
+ }
+
+
+def map_network_function_virtualisation(nren: NREN, year: int, answers: Dict[str, Any]):
+ nfv = answers.get("nfv")
+ if nfv:
+ nfv_types = answers.get("nfv_types", [])
+ if "other" in nfv_types:
+ nfv_types.remove("other")
+ nfv_types.append(answers.get("nfv_types-Comment", "other"))
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'nfv': YesNoPlanned[nfv.lower()],
+ 'nfv_specifics': nfv_types
+ }
+
+
+def map_network_automation(nren: NREN, year: int, answers: Dict[str, Any]):
+ network_automation = answers.get("network_automation")
+ if network_automation:
+ network_automation_tasks = answers.get("network_automation_tasks", [])
+ return {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'network_automation': YesNoPlanned[network_automation.lower()],
+ 'network_automation_specifics': network_automation_tasks
+ }
+
+
+def map_nren_services(nren: NREN, year: int, answers: Dict[str, Any]):
+ all_services = {}
+ for question_name in [
+ "services_collaboration", "services_hosting", "services_identity", "services_isp",
+ "services_multimedia", "services_network", "services_professional", "services_security"
+ ]:
+ all_services.update(answers.get(question_name, {}))
+
+ for service_key, service_answers in all_services.items():
+ offered = service_answers.get("offered")
+ if offered == ["yes"]:
+ yield {
+ 'nren_id': nren.id,
+ 'year': year,
+ 'service_key': service_key,
+ 'product_name': service_answers.get("name", ""),
+ 'additional_information': service_answers.get("additional_information", ""),
+ 'official_description': service_answers.get("description", "")
+ }
+
+
+def map_2023(responses: List[SurveyResponse]) -> Dict[Type[PresentationModel], List[Dict]]:
+ year = 2023
+
+ result = defaultdict(list) # {model: [data]}
+
+ for response in responses:
+ nren = response.nren
+ answers = response.answers['data']
+
+ budget = map_budget_entry(year, nren, answers)
+ if budget:
+ result[BudgetEntry].append(budget)
+
+ funding = map_fundingsource_entry(year, nren, answers)
+ if funding:
+ result[FundingSource].append(funding)
+
+ charging = map_charging_structure_entry(year, nren, answers)
+ if charging:
+ result[ChargingStructure].append(charging)
+
+ nren_staff = map_nren_staff(year, nren, answers)
+ if nren_staff:
+ result[NrenStaff].append(nren_staff)
+
+ parent = map_parent_orgs(nren, year, answers)
+ if parent:
+ result[ParentOrganization].append(parent)
+
+ subs = map_sub_orgs(nren, year, answers)
+ for sub in subs:
+ result[SubOrganization].append(sub)
+
+ ec_projects = map_ec_projects(nren, year, answers)
+ for ec_project in ec_projects:
+ result[ECProject].append(ec_project)
+
+ policy = map_policy(nren, year, answers)
+ if policy:
+ result[Policy].append(policy)
+
+ traffic_volume = map_traffic_volume(nren, year, answers)
+ if traffic_volume:
+ result[TrafficVolume].append(traffic_volume)
+
+ institution_urls = map_institution_urls(nren, year, answers)
+ if institution_urls:
+ result[InstitutionURLs].append(institution_urls)
+
+ central_procurement = map_central_procurement(nren, year, answers)
+ if central_procurement:
+ result[CentralProcurement].append(central_procurement)
+
+ service_management = map_service_management(nren, year, answers)
+ if service_management:
+ result[ServiceManagement].append(service_management)
+
+ service_user_types = map_service_user_types(nren, year, answers)
+ for service_user_type in service_user_types:
+ result[ServiceUserTypes].append(service_user_type)
+
+ eosc_listings = map_eosc_listings(nren, year, answers)
+ if eosc_listings:
+ result[EOSCListings].append(eosc_listings)
+
+ standards = map_standards(nren, year, answers)
+ if standards:
+ result[Standards].append(standards)
+
+ crisis_exercises = map_crisis_exercises(nren, year, answers)
+ if crisis_exercises:
+ result[CrisisExercises].append(crisis_exercises)
+
+ security_controls = map_security_controls(nren, year, answers)
+ if security_controls:
+ result[SecurityControls].append(security_controls)
+
+ connected_proportions = map_connected_proportion(nren, year, answers)
+ for connected_proportion in connected_proportions:
+ result[ConnectedProportion].append(connected_proportion)
+
+ connectivity_levels = map_connectivity_levels(nren, year, answers)
+ for connectivity_level in connectivity_levels:
+ result[ConnectivityLevel].append(connectivity_level)
+
+ connection_carriers = map_connection_carriers(nren, year, answers)
+ for connection_carrier in connection_carriers:
+ result[ConnectionCarrier].append(connection_carrier)
+
+ connectivity_loads = map_connectivity_loads(nren, year, answers)
+ for connectivity_load in connectivity_loads:
+ result[ConnectivityLoad].append(connectivity_load)
+
+ connectivity_growths = map_connectivity_growth(nren, year, answers)
+ for connectivity_growth in connectivity_growths:
+ result[ConnectivityGrowth].append(connectivity_growth)
+
+ commercial_connectivities = map_commercial_connectivity(nren, year, answers)
+ for commercial_connectivity in commercial_connectivities:
+ result[CommercialConnectivity].append(commercial_connectivity)
+
+ commercial_charging_levels = map_commercial_charging_levels(nren, year, answers)
+ for commercial_charging_level in commercial_charging_levels:
+ result[CommercialChargingLevel].append(commercial_charging_level)
+
+ remote_campuses = map_remote_campuses(nren, year, answers)
+ if remote_campuses:
+ result[RemoteCampuses].append(remote_campuses)
+
+ dark_fibre_lease = map_dark_fibre_lease(nren, year, answers)
+ if dark_fibre_lease:
+ result[DarkFibreLease].append(dark_fibre_lease)
+
+ dark_fibre_installed = map_dark_fibre_installed(nren, year, answers)
+ if dark_fibre_installed:
+ result[DarkFibreInstalled].append(dark_fibre_installed)
+
+ fibre_light = map_fibre_light(nren, year, answers)
+ if fibre_light:
+ result[FibreLight].append(fibre_light)
+
+ network_map_urls = map_network_map_urls(nren, year, answers)
+ if network_map_urls:
+ result[NetworkMapUrls].append(network_map_urls)
+
+ monitoring_tools = map_monitoring_tools(nren, year, answers)
+ if monitoring_tools:
+ result[MonitoringTools].append(monitoring_tools)
+
+ passive_monitoring = map_passive_monitoring(nren, year, answers)
+ if passive_monitoring:
+ result[PassiveMonitoring].append(passive_monitoring)
+
+ traffic_statistics = map_traffic_statistics(nren, year, answers)
+ if traffic_statistics:
+ result[TrafficStatistics].append(traffic_statistics)
+
+ siem_vendors = map_siem_vendors(nren, year, answers)
+ if siem_vendors:
+ result[SiemVendors].append(siem_vendors)
+
+ certificate_providers = map_certificate_providers(nren, year, answers)
+ if certificate_providers:
+ result[CertificateProviders].append(certificate_providers)
+
+ weathermap = map_weathermap_entry(nren, year, answers)
+ if weathermap:
+ result[WeatherMap].append(weathermap)
+
+ pert_team = map_pert_team(nren, year, answers)
+ if pert_team:
+ result[PertTeam].append(pert_team)
+
+ alienwave = map_alienwave_entry(nren, year, answers)
+ if alienwave:
+ result[AlienWave].append(alienwave)
+
+ capacity = map_capacity_entry(nren, year, answers)
+ if capacity:
+ result[Capacity].append(capacity)
+
+ external_connections = map_external_connections(nren, year, answers)
+ if external_connections:
+ result[ExternalConnections].append(external_connections)
+
+ non_re_peers = map_non_re_peers(nren, year, answers)
+ if non_re_peers:
+ result[NonREPeers].append(non_re_peers)
+
+ traffic_ratio = map_traffic_ratio(nren, year, answers)
+ if traffic_ratio:
+ result[TrafficRatio].append(traffic_ratio)
+
+ ops_automation = map_ops_automation(nren, year, answers)
+ if ops_automation:
+ result[OpsAutomation].append(ops_automation)
+
+ nfv = map_network_function_virtualisation(nren, year, answers)
+ if nfv:
+ result[NetworkFunctionVirtualisation].append(nfv)
+
+ network_automation = map_network_automation(nren, year, answers)
+ if network_automation:
+ result[NetworkAutomation].append(network_automation)
+
+ nren_services = map_nren_services(nren, year, answers)
+ for nren_service in nren_services:
+ result[NRENService].append(nren_service)
+
+ return result
diff --git a/compendium_v2/publishers/year/map_2024.py b/compendium_v2/publishers/year/map_2024.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/test_survey_publisher.py b/test/test_survey_publisher.py
index 140356e2..5b9eb9d3 100644
--- a/test/test_survey_publisher.py
+++ b/test/test_survey_publisher.py
@@ -6,7 +6,8 @@ from sqlalchemy import func, select
from compendium_v2 import db
from compendium_v2.db import presentation_model_enums as model_enums, presentation_models
-from compendium_v2.publishers.survey_publisher import _map_2023
+from compendium_v2.db.survey_models import Survey, SurveyResponse, ResponseStatus, SurveyStatus
+from compendium_v2.publishers.survey_publisher import publish
JSON_FILE = os.path.join(os.path.dirname(__file__), "data", "2023_all_questions_answered.json")
@@ -17,13 +18,13 @@ def test_v2_publisher_empty(app):
with app.app_context():
nren = presentation_models.NREN(name='name', country='country')
+ db.session.add(nren)
+ survey = Survey(year=2023, survey={}, status=SurveyStatus.open)
+ response = SurveyResponse(survey=survey, nren=nren, answers={"data": data}, status=ResponseStatus.completed)
+ db.session.add(survey)
+ db.session.add(response)
db.session.commit()
- with app.app_context():
- _map_2023(nren, {"data": data})
- db.session.commit()
-
- with app.app_context():
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
@@ -35,13 +36,15 @@ def test_v2_publisher_full(app):
with app.app_context():
nren = presentation_models.NREN(name='name', country='country')
+ db.session.add(nren)
+ survey = Survey(year=2023, survey={}, status=SurveyStatus.open)
+ response = SurveyResponse(survey=survey, nren=nren, answers={"data": data}, status=ResponseStatus.completed)
+ db.session.add(survey)
+ db.session.add(response)
db.session.commit()
- with app.app_context():
- _map_2023(nren, {"data": data})
- db.session.commit()
+ publish(2023)
- with app.app_context():
budget = db.session.scalar(select(presentation_models.BudgetEntry.budget))
assert budget == Decimal("124.76")
--
GitLab