Skip to content
Snippets Groups Projects
Commit 9b94e463 authored by Saket Agrahari's avatar Saket Agrahari
Browse files

Merge branch 'feature/COMP-328' into 'develop'

IRU Duration - Network Section

See merge request !119
parents 9ec548ba d2c64f8d
No related branches found
No related tags found
1 merge request!119IRU Duration - Network Section
Showing
with 277 additions and 12 deletions
......@@ -36,6 +36,7 @@ import NetworkSiemVendorsPage from "./pages/NetworkSiemVendors";
import NetworkCapacityLargestLinkPage from "./pages/NetworkCapacityLargestLink";
import NetworkCapacityCoreIPPage from "./pages/NetworkCapacityCoreIP";
import NetworkNonRAndEPeerPage from "./pages/NetworkNonRAndEPeer";
import NetworkIRUDurationPage from "./pages/NetworkIRUDuration";
const router = createBrowserRouter([
{ path: "/budget", element: <BudgetPage /> },
......@@ -83,6 +84,7 @@ const router = createBrowserRouter([
{ path: "/capacity-largest-link", element: <NetworkCapacityLargestLinkPage /> },
{ path: "/capacity-core-ip", element: <NetworkCapacityCoreIPPage /> },
{ path: "/non-rne-peers", element: <NetworkNonRAndEPeerPage /> },
{ path: "/iru-duration", element: <NetworkIRUDurationPage /> },
{ path: "*", element: <Landing /> },
]);
......
......@@ -106,6 +106,14 @@ export interface NonRAndEPeers extends NrenAndYearDatapoint {
nr_of_non_r_and_e_peers: (number | null),
}
export interface DarkFibreLease extends NrenAndYearDatapoint {
iru_or_lease: boolean,
fibre_length_in_country: (number | null),
fibre_length_outside_country: (number | null),
iru_duration: (number | null),
}
export interface NetworkMapUrls extends NrenAndYearDatapoint {
weather_map: boolean,
urls: (string | null)[]
......
......@@ -13,6 +13,11 @@ const NetworkSidebar = () => {
<span>Traffic volume</span>
</Link>
</Row>
<Row>
<Link to="/iru-duration" className="link-text-underline">
<span>Average Duration of IRU leases of Fibre by NRENs</span>
</Link>
</Row>
<Row>
<Link to="/fibre-light" className="link-text-underline">
<span>Approaches to lighting NREN fibre networks</span>
......
......@@ -551,7 +551,7 @@ export const createNetworkCapacityDataLookUp = <Datatype extends NrenAndYearData
if (!dataForNren) {
return 0
}
console.log('dataForNren-->',dataForNren)
// console.log('dataForNren-->',dataForNren)
return (dataForNren[columnProperty] ?? 0);
}),
......@@ -588,4 +588,30 @@ export const createNetworkCapacityDataLookUp = <Datatype extends NrenAndYearData
}
return dataset;
}
\ No newline at end of file
}
export function createNetworkDarkFibreDataLookUp<Datatype extends NrenAndYearDatapoint>(budgetEntries: Datatype[], columnProperty: string,) : BasicDataset {
const labelsYear = [...new Set(budgetEntries.map((item) => item.year))].sort();
const labelsNREN = [...new Set(budgetEntries.map((item) => item.nren))].sort();
const dataLookup = createDataLookup(budgetEntries);
const sets = labelsNREN.map(nren => {
const randomColor = stringToColour(nren);
return {
backgroundColor: randomColor,
borderColor: randomColor,
data: labelsYear.map((year) => {
const dataForNren = dataLookup.get(nren)?.get(year);
return dataForNren ? dataForNren[columnProperty] ?? null : null;
}),
label: nren,
hidden: false
}
});
return {
datasets: sets,
labels: labelsYear.map(year => year.toString())
};
}
\ No newline at end of file
......@@ -144,6 +144,11 @@ function CompendiumData(): ReactElement {
<span>Traffic volume</span>
</Link>
</Row>
<Row>
<Link to="/iru-duration" className="link-text-underline">
<span>Average Duration of IRU leases of Fibre by NRENs</span>
</Link>
</Row>
<Row>
<Link to="/fibre-light" className="link-text-underline">
<span>Approaches to lighting NREN fibre networks</span>
......
......@@ -121,7 +121,7 @@ function NetworkCapacityCoreIPPage() {
NREN networks, expressed in Gbit/s. It refers to the circuit capacity, not the traffic over
the network.`
const filename = "number_of_nren_employees";
const filename = "capacity_core_ip";
return (
<DataPage title={title}
description={description}
......
......@@ -121,7 +121,7 @@ function NetworkCapacityLargestLinkPage() {
their network used for internet traffic (either shared or dedicated). While they were invited to
provide the sum of aggregated links, backup capacity was not to be included.`
const filename = "number_of_nren_employees";
const filename = "capacity_largest_link";
return (
<DataPage title={title}
description={description}
......
import React, { ReactElement, useContext } from 'react';
import { Row } from "react-bootstrap";
import { Line } from 'react-chartjs-2';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { DarkFibreLease } from "../Schema";
import { createNetworkDarkFibreDataLookUp } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter";
import { Sections } from '../helpers/constants';
import { FilterSelectionContext } from '../helpers/FilterSelectionProvider';
import ChartContainer from "../components/graphing/ChartContainer";
import { useData } from '../helpers/useData';
const options = {
responsive: true,
animation: {
duration: 0
},
plugins: {
legend: {
display: false,
position: 'top' as const,
onClick: () => { /* intentionally empty */ },
},
title: {
display: true,
text: '',
},
// add M € to tooltip
tooltip: {
callbacks: {
label: function (tooltipItem) {
let label = tooltipItem.dataset.label || '';
if (tooltipItem.parsed.y !== null) {
label += `: ${tooltipItem.parsed.y} year(s)`
}
return label;
}
},
},
},
scales: {
y: {
ticks: {
callback: (value: string | number) => {
return `${value} year(s)`;
},
},
},
},
};
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
function NetworkIRUDurationPage(): ReactElement {
const { filterSelection, setFilterSelection } = useContext(FilterSelectionContext);
const { data: iruDurationResponse, nrens } = useData<DarkFibreLease>('/api/network/dark-fibre', setFilterSelection);
const iruDurationFilteredResponse = iruDurationResponse
.filter(data => data.iru_duration !== null && data.iru_duration !== undefined);
const iruDurationData = createNetworkDarkFibreDataLookUp(iruDurationFilteredResponse,'iru_duration');
const selectedData = iruDurationFilteredResponse.filter(data =>
filterSelection.selectedNrens.includes(data.nren) // we only allow filtering nrens for this page
);
iruDurationData.datasets.forEach(dataset => {
dataset.hidden = !filterSelection.selectedNrens.includes(dataset.label);
});
const filterNode = <Filter
filterOptions={{ availableYears: [], availableNrens: [...nrens.values()] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
return (
<DataPage title="Average Duration of IRU leases of Fibre by NRENs "
description={<span>NRENs sometimes take out an IRU (Indefeasible Right of Use),
which is essentially a long-term lease, on a portion of the capacity of a cable
rather than laying cable themselves. This graph shows the average duration,
in years, of the IRUs of NRENs.</span>}
category={Sections.Network} filter={filterNode}
data={selectedData} filename="iru_duration_data">
<>
<Row>
<ChartContainer>
<Line data={iruDurationData} options={options} />
</ChartContainer>
</Row>
</>
</DataPage>
);
}
export default NetworkIRUDurationPage;
......@@ -19,7 +19,7 @@ function NetworkMapUrlPage(): React.ReactElement {
);
const dataLookup = createDataLookup(selectedData);
console.log('dataLookup-->', dataLookup);
// console.log('dataLookup-->', dataLookup);
const filterNode = <Filter
filterOptions={{ availableYears: [...years], availableNrens: [...nrens.values()] }}
......
......@@ -119,7 +119,7 @@ function NetworkNonRAndEPeerPage() {
const description = `The graph below shows the number of non-Research and Education networks
NRENs peer with. This includes all direct IP-peerings to commercial networks, eg. Google`
const filename = "number_of_nren_employees";
const filename = "non_r_and_e_peering";
return (
<DataPage title={title}
description={description}
......
......@@ -35,7 +35,7 @@ function NetworkPertTeam(): React.ReactElement {
description="Some NRENs have an in-house Performance Enhancement Response Team,
or PERT, to investigate network performance issues."
category={Sections.Network} filter={filterNode}
data={selectedData} filename="fibre_light_of_nrens_per_year">
data={selectedData} filename="pert_team_nrens_per_year">
<>
<ChartContainer>
<Table className="charging-struct-table" striped bordered >
......
......@@ -34,7 +34,7 @@ function NetworkSiemVendorsPage(): React.ReactElement {
<DataPage title="Certification Services used by NRENs "
description="The table below shows the kinds of Network Certificate Providers used by NRENs."
category={Sections.Network} filter={filterNode}
data={selectedData} filename="certificate_provider_nrens_per_year">
data={selectedData} filename="siem_vendor_nrens_per_year">
<>
<ChartContainer>
<Table className="charging-struct-table" striped bordered >
......
......@@ -2,7 +2,7 @@ from typing import Any
from compendium_v2.db.presentation_models import PertTeam, PassiveMonitoring, AlienWave, OpsAutomation, \
NetworkAutomation, TrafficStatistics, WeatherMap, NetworkMapUrls, NetworkFunctionVirtualisation, \
CertificateProviders, SiemVendors, Capacity, NonREPeers
CertificateProviders, SiemVendors, Capacity, NonREPeers, DarkFibreLease
from compendium_v2.routes import common
from flask import Blueprint, jsonify
......@@ -264,6 +264,28 @@ NON_R_AND_E_PEERS_RESPONSE_SCHEMA = {
'items': {'$ref': '#/definitions/non_r_and_e_peers'}
}
DARK_FIBRE_LEASE_RESPONSE_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {
'dark_fibre': {
'type': 'object',
'properties': {
'nren': {'type': 'string'},
'nren_country': {'type': 'string'},
'year': {'type': 'integer'},
'iru_or_lease': {'type': 'string'},
'fibre_length_in_country': {'type': 'integer'},
'fibre_length_outside_country': {'type': 'integer'},
'iru_duration': {'type': 'string'},
},
'required': ['nren', 'nren_country', 'year'],
'additionalProperties': False
}
},
'type': 'array',
'items': {'$ref': '#/definitions/dark_fibre'}
}
def pert_team_extract_data(pert_team: PertTeam) -> dict:
return {
......@@ -737,3 +759,42 @@ def non_rne_peers_data_view() -> Any:
entries.append(non_rne_peers_extract_data(entry))
return jsonify(entries)
def dark_fibre_lease_extract_data(
dark_fibre_lease: DarkFibreLease) -> dict:
return {
'nren': dark_fibre_lease.nren.name,
'nren_country': dark_fibre_lease.nren.country,
'year': int(dark_fibre_lease.year),
'iru_or_lease': str(dark_fibre_lease.iru_or_lease),
'fibre_length_in_country': dark_fibre_lease.fibre_length_in_country,
'fibre_length_outside_country': dark_fibre_lease.fibre_length_outside_country,
'iru_duration': dark_fibre_lease.iru_duration,
}
@routes.route('/dark-fibre', methods=['GET'])
@common.require_accepts_json
def dark_fibre_lease_data_view() -> Any:
"""
handler for /api/network/dark-fibre requests
Endpoint for getting the fibre operation models the NREN.
This endpoint retrieves fibre operation model that of the NREN.
response will be formatted as:
.. asjson::
compendium_v2.routes.network.NON_R_AND_E_PEERS_RESPONSE_SCHEMA
:return:
"""
entries = []
records = common.get_data(DarkFibreLease)
for entry in records:
entries.append(dark_fibre_lease_extract_data(entry))
return jsonify(entries)
This diff is collapsed.
......@@ -676,6 +676,30 @@ def test_non_rne_peers_data(app):
db.session.commit()
@pytest.fixture
def test_dark_fibre_lease_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: 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(presentation_models.DarkFibreLease(
nren=nren,
year=year,
iru_or_lease=True,
fibre_length_in_country=5,
fibre_length_outside_country=5,
iru_duration=6,
))
db.session.commit()
@pytest.fixture
def test_traffic_data(app):
with app.app_context():
......
......@@ -7,7 +7,8 @@ from compendium_v2.routes.network import PERT_TEAM_RESPONSE_SCHEMA, PASSIVE_MONI
ALIEN_WAVE_RESPONSE_SCHEMA, OPS_AUTOMATION_RESPONSE_SCHEMA, NETWORK_AUTOMATION_RESPONSE_SCHEMA, \
TRAFFIC_STATISTICS_RESPONSE_SCHEMA, WEATHER_MAP_RESPONSE_SCHEMA, NETWORK_MAP_URLS_RESPONSE_SCHEMA, \
NETWORK_FUNCTION_VIRTUALISATION_RESPONSE_SCHEMA, CERTIFICATE_PROVIDER_RESPONSE_SCHEMA, \
SIEM_VENDORS_RESPONSE_SCHEMA, CAPACITY_RESPONSE_SCHEMA, NON_R_AND_E_PEERS_RESPONSE_SCHEMA
SIEM_VENDORS_RESPONSE_SCHEMA, CAPACITY_RESPONSE_SCHEMA, NON_R_AND_E_PEERS_RESPONSE_SCHEMA, \
DARK_FIBRE_LEASE_RESPONSE_SCHEMA
def test_pert_team_response(client, test_pert_team_data):
......@@ -158,3 +159,13 @@ def test_non_rne_peers_response(client, test_non_rne_peers_data):
result = json.loads(rv.data.decode('utf-8'))
jsonschema.validate(result, NON_R_AND_E_PEERS_RESPONSE_SCHEMA)
assert result
def test_dark_fibre_lease_response(client, test_dark_fibre_lease_data):
rv = client.get(
'/api/network/dark-fibre',
headers={'Accept': ['application/json']})
assert rv.status_code == 200
result = json.loads(rv.data.decode('utf-8'))
jsonschema.validate(result, DARK_FIBRE_LEASE_RESPONSE_SCHEMA)
assert result
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment