Add IPADDRESSV4 export and update data assets
Browse files
apps/sector_kml_generator.py
CHANGED
|
@@ -182,7 +182,7 @@ def _show_site_position_map(
|
|
| 182 |
fig = go.Figure()
|
| 183 |
mode = "markers+text" if show_labels else "markers"
|
| 184 |
fig.add_trace(
|
| 185 |
-
go.
|
| 186 |
lat=map_df[lat_col],
|
| 187 |
lon=map_df[lon_col],
|
| 188 |
mode=mode,
|
|
@@ -197,12 +197,12 @@ def _show_site_position_map(
|
|
| 197 |
|
| 198 |
fig.update_layout(
|
| 199 |
title=title,
|
| 200 |
-
|
| 201 |
-
|
| 202 |
"lat": float(map_df[lat_col].mean()),
|
| 203 |
"lon": float(map_df[lon_col].mean()),
|
| 204 |
},
|
| 205 |
-
|
| 206 |
height=620,
|
| 207 |
margin={"r": 0, "t": 45, "l": 0, "b": 0},
|
| 208 |
)
|
|
@@ -234,7 +234,7 @@ def _show_sector_map(df: pd.DataFrame, show_labels: bool = True) -> None:
|
|
| 234 |
lats = [coord[1] for coord in coords]
|
| 235 |
rgba = kml_color_to_rgba(row["color"])
|
| 236 |
fig.add_trace(
|
| 237 |
-
go.
|
| 238 |
lon=lons,
|
| 239 |
lat=lats,
|
| 240 |
mode="lines",
|
|
@@ -251,7 +251,7 @@ def _show_sector_map(df: pd.DataFrame, show_labels: bool = True) -> None:
|
|
| 251 |
site_df = map_df.drop_duplicates(subset=["code"]).copy()
|
| 252 |
mode = "markers+text" if show_labels else "markers"
|
| 253 |
fig.add_trace(
|
| 254 |
-
go.
|
| 255 |
lat=site_df["Latitude"],
|
| 256 |
lon=site_df["Longitude"],
|
| 257 |
mode=mode,
|
|
@@ -266,12 +266,12 @@ def _show_sector_map(df: pd.DataFrame, show_labels: bool = True) -> None:
|
|
| 266 |
|
| 267 |
fig.update_layout(
|
| 268 |
title="Sector map preview",
|
| 269 |
-
|
| 270 |
-
|
| 271 |
"lat": float(map_df["Latitude"].mean()),
|
| 272 |
"lon": float(map_df["Longitude"].mean()),
|
| 273 |
},
|
| 274 |
-
|
| 275 |
height=650,
|
| 276 |
margin={"r": 0, "t": 45, "l": 0, "b": 0},
|
| 277 |
)
|
|
|
|
| 182 |
fig = go.Figure()
|
| 183 |
mode = "markers+text" if show_labels else "markers"
|
| 184 |
fig.add_trace(
|
| 185 |
+
go.Scattermap(
|
| 186 |
lat=map_df[lat_col],
|
| 187 |
lon=map_df[lon_col],
|
| 188 |
mode=mode,
|
|
|
|
| 197 |
|
| 198 |
fig.update_layout(
|
| 199 |
title=title,
|
| 200 |
+
map_style="open-street-map",
|
| 201 |
+
map_center={
|
| 202 |
"lat": float(map_df[lat_col].mean()),
|
| 203 |
"lon": float(map_df[lon_col].mean()),
|
| 204 |
},
|
| 205 |
+
map_zoom=_estimate_zoom(map_df, lat_col, lon_col),
|
| 206 |
height=620,
|
| 207 |
margin={"r": 0, "t": 45, "l": 0, "b": 0},
|
| 208 |
)
|
|
|
|
| 234 |
lats = [coord[1] for coord in coords]
|
| 235 |
rgba = kml_color_to_rgba(row["color"])
|
| 236 |
fig.add_trace(
|
| 237 |
+
go.Scattermap(
|
| 238 |
lon=lons,
|
| 239 |
lat=lats,
|
| 240 |
mode="lines",
|
|
|
|
| 251 |
site_df = map_df.drop_duplicates(subset=["code"]).copy()
|
| 252 |
mode = "markers+text" if show_labels else "markers"
|
| 253 |
fig.add_trace(
|
| 254 |
+
go.Scattermap(
|
| 255 |
lat=site_df["Latitude"],
|
| 256 |
lon=site_df["Longitude"],
|
| 257 |
mode=mode,
|
|
|
|
| 266 |
|
| 267 |
fig.update_layout(
|
| 268 |
title="Sector map preview",
|
| 269 |
+
map_style="open-street-map",
|
| 270 |
+
map_center={
|
| 271 |
"lat": float(map_df["Latitude"].mean()),
|
| 272 |
"lon": float(map_df["Longitude"].mean()),
|
| 273 |
},
|
| 274 |
+
map_zoom=_estimate_zoom(map_df, "Latitude", "Longitude"),
|
| 275 |
height=650,
|
| 276 |
margin={"r": 0, "t": 45, "l": 0, "b": 0},
|
| 277 |
)
|
data/kpi_health_check_profiles/Profil_1.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
{
|
| 2 |
-
"name": "
|
| 3 |
-
"saved_at": "
|
| 4 |
"config": {
|
|
|
|
| 5 |
"analysis_range": [
|
| 6 |
null,
|
| 7 |
null
|
|
@@ -10,23 +11,31 @@
|
|
| 10 |
"recent_days": 7,
|
| 11 |
"rel_threshold_pct": 10.0,
|
| 12 |
"min_consecutive_days": 3,
|
|
|
|
| 13 |
"min_criticality": 0,
|
| 14 |
"min_anomaly_score": 0,
|
| 15 |
"city_filter": "",
|
| 16 |
"top_rat_filter": [
|
| 17 |
"2G",
|
| 18 |
"3G",
|
| 19 |
-
"LTE"
|
|
|
|
| 20 |
],
|
| 21 |
"top_status_filter": [
|
| 22 |
"DEGRADED",
|
| 23 |
"PERSISTENT_DEGRADED"
|
| 24 |
],
|
| 25 |
-
"preset_selected": "presets_1
|
| 26 |
"drilldown": {
|
| 27 |
-
"site_code": 2130,
|
| 28 |
"rat": "LTE",
|
| 29 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
}
|
| 31 |
}
|
| 32 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"name": "profil_1",
|
| 3 |
+
"saved_at": "2026-03-30T10:24:07.600473Z",
|
| 4 |
"config": {
|
| 5 |
+
"granularity": "Hourly",
|
| 6 |
"analysis_range": [
|
| 7 |
null,
|
| 8 |
null
|
|
|
|
| 11 |
"recent_days": 7,
|
| 12 |
"rel_threshold_pct": 10.0,
|
| 13 |
"min_consecutive_days": 3,
|
| 14 |
+
"only_complaint_sites": false,
|
| 15 |
"min_criticality": 0,
|
| 16 |
"min_anomaly_score": 0,
|
| 17 |
"city_filter": "",
|
| 18 |
"top_rat_filter": [
|
| 19 |
"2G",
|
| 20 |
"3G",
|
| 21 |
+
"LTE",
|
| 22 |
+
"TWAMP"
|
| 23 |
],
|
| 24 |
"top_status_filter": [
|
| 25 |
"DEGRADED",
|
| 26 |
"PERSISTENT_DEGRADED"
|
| 27 |
],
|
| 28 |
+
"preset_selected": "presets_1",
|
| 29 |
"drilldown": {
|
|
|
|
| 30 |
"rat": "LTE",
|
| 31 |
+
"site_value": "",
|
| 32 |
+
"kpi_group": "All (selected KPIs)",
|
| 33 |
+
"kpi_group_mode": "",
|
| 34 |
+
"kpi": "% MIMO RI 2",
|
| 35 |
+
"compare_kpis": [],
|
| 36 |
+
"compare_norm": "None",
|
| 37 |
+
"show_sla": true,
|
| 38 |
+
"corr_window": "Recent"
|
| 39 |
}
|
| 40 |
}
|
| 41 |
}
|
physical_db/physical_database.csv
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
queries/process_all_db.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from queries.process_atoll_db import process_data_for_atoll
|
| 2 |
from queries.process_gsm import combined_gsm_database, gsm_analaysis
|
| 3 |
from queries.process_invunit import process_invunit_data, process_invunit_number_data
|
|
|
|
| 4 |
from queries.process_lte import lte_fdd_analaysis, lte_tdd_analaysis, process_lte_data
|
| 5 |
from queries.process_mrbts import process_mrbts_data
|
| 6 |
from queries.process_nice_db import process_data_for_nice
|
|
@@ -33,21 +34,11 @@ def all_dbs(filepath: str):
|
|
| 33 |
def process_all_tech_db(filepath: str):
|
| 34 |
all_dbs(filepath)
|
| 35 |
site_db()
|
|
|
|
| 36 |
|
| 37 |
UtilsVars.final_all_database = convert_database_dfs(
|
| 38 |
UtilsVars.all_db_dfs,
|
| 39 |
-
|
| 40 |
-
"GSM",
|
| 41 |
-
"MAL",
|
| 42 |
-
"TRX",
|
| 43 |
-
"WCDMA",
|
| 44 |
-
"LTE_FDD",
|
| 45 |
-
"LTE_TDD",
|
| 46 |
-
"MRBTS",
|
| 47 |
-
"INVUNIT",
|
| 48 |
-
"INVUNIT_NUMBER",
|
| 49 |
-
"SITE",
|
| 50 |
-
],
|
| 51 |
)
|
| 52 |
|
| 53 |
|
|
@@ -64,20 +55,10 @@ def process_all_tech_db_with_stats(
|
|
| 64 |
)
|
| 65 |
lte_fdd_analaysis(filepath)
|
| 66 |
lte_tdd_analaysis(filepath)
|
|
|
|
| 67 |
UtilsVars.final_all_database = convert_database_dfs(
|
| 68 |
UtilsVars.all_db_dfs,
|
| 69 |
-
|
| 70 |
-
"GSM",
|
| 71 |
-
"MAL",
|
| 72 |
-
"TRX",
|
| 73 |
-
"WCDMA",
|
| 74 |
-
"LTE_FDD",
|
| 75 |
-
"LTE_TDD",
|
| 76 |
-
"MRBTS",
|
| 77 |
-
"INVUNIT",
|
| 78 |
-
"INVUNIT_NUMBER",
|
| 79 |
-
"SITE",
|
| 80 |
-
],
|
| 81 |
)
|
| 82 |
|
| 83 |
|
|
|
|
| 1 |
from queries.process_atoll_db import process_data_for_atoll
|
| 2 |
from queries.process_gsm import combined_gsm_database, gsm_analaysis
|
| 3 |
from queries.process_invunit import process_invunit_data, process_invunit_number_data
|
| 4 |
+
from queries.process_ipaddressv4 import process_ipaddressv4_data
|
| 5 |
from queries.process_lte import lte_fdd_analaysis, lte_tdd_analaysis, process_lte_data
|
| 6 |
from queries.process_mrbts import process_mrbts_data
|
| 7 |
from queries.process_nice_db import process_data_for_nice
|
|
|
|
| 34 |
def process_all_tech_db(filepath: str):
|
| 35 |
all_dbs(filepath)
|
| 36 |
site_db()
|
| 37 |
+
process_ipaddressv4_data(filepath)
|
| 38 |
|
| 39 |
UtilsVars.final_all_database = convert_database_dfs(
|
| 40 |
UtilsVars.all_db_dfs,
|
| 41 |
+
UtilsVars.all_db_dfs_names,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
)
|
| 43 |
|
| 44 |
|
|
|
|
| 55 |
)
|
| 56 |
lte_fdd_analaysis(filepath)
|
| 57 |
lte_tdd_analaysis(filepath)
|
| 58 |
+
process_ipaddressv4_data(filepath)
|
| 59 |
UtilsVars.final_all_database = convert_database_dfs(
|
| 60 |
UtilsVars.all_db_dfs,
|
| 61 |
+
UtilsVars.all_db_dfs_names,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
)
|
| 63 |
|
| 64 |
|
queries/process_ipaddressv4.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
|
| 3 |
+
from utils.dump_excel import read_dump_excel
|
| 4 |
+
from utils.utils_vars import UtilsVars
|
| 5 |
+
|
| 6 |
+
IPADDRESSV4_SHEET_NAME = "IPADDRESSV4"
|
| 7 |
+
IPADDRESSV4_COLUMNS = ["MRBTS", "localIpAddr"]
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _sheet_exists(file_path, sheet_name: str) -> bool:
|
| 11 |
+
if hasattr(file_path, "seek"):
|
| 12 |
+
file_path.seek(0)
|
| 13 |
+
|
| 14 |
+
with pd.ExcelFile(file_path, engine="calamine") as workbook:
|
| 15 |
+
exists = sheet_name in workbook.sheet_names
|
| 16 |
+
|
| 17 |
+
if hasattr(file_path, "seek"):
|
| 18 |
+
file_path.seek(0)
|
| 19 |
+
|
| 20 |
+
return exists
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _normalize_mrbts(series: pd.Series) -> pd.Series:
|
| 24 |
+
normalized = series.astype("string").str.strip()
|
| 25 |
+
return normalized.str.replace(r"\.0+$", "", regex=True)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def process_ipaddressv4_data(file_path: str) -> pd.DataFrame:
|
| 29 |
+
if not _sheet_exists(file_path, IPADDRESSV4_SHEET_NAME):
|
| 30 |
+
return pd.DataFrame(columns=IPADDRESSV4_COLUMNS)
|
| 31 |
+
|
| 32 |
+
dfs = read_dump_excel(
|
| 33 |
+
file_path,
|
| 34 |
+
sheet_name=[IPADDRESSV4_SHEET_NAME],
|
| 35 |
+
expected_columns=IPADDRESSV4_COLUMNS,
|
| 36 |
+
)
|
| 37 |
+
df_ipaddressv4 = dfs[IPADDRESSV4_SHEET_NAME].copy()
|
| 38 |
+
df_ipaddressv4.columns = df_ipaddressv4.columns.str.replace(r"[ ]", "", regex=True)
|
| 39 |
+
|
| 40 |
+
missing_columns = [
|
| 41 |
+
column for column in IPADDRESSV4_COLUMNS if column not in df_ipaddressv4.columns
|
| 42 |
+
]
|
| 43 |
+
if missing_columns:
|
| 44 |
+
raise ValueError(
|
| 45 |
+
"IPADDRESSV4 sheet is missing required columns: "
|
| 46 |
+
+ ", ".join(missing_columns)
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
df_ipaddressv4 = df_ipaddressv4[IPADDRESSV4_COLUMNS].copy()
|
| 50 |
+
df_ipaddressv4["MRBTS"] = _normalize_mrbts(df_ipaddressv4["MRBTS"])
|
| 51 |
+
df_ipaddressv4["localIpAddr"] = (
|
| 52 |
+
df_ipaddressv4["localIpAddr"].astype("string").str.strip()
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
UtilsVars.all_db_dfs.append(df_ipaddressv4)
|
| 56 |
+
UtilsVars.all_db_dfs_names.append(IPADDRESSV4_SHEET_NAME)
|
| 57 |
+
return df_ipaddressv4
|
tests/test_process_ipaddressv4.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
from queries.process_ipaddressv4 import process_ipaddressv4_data
|
| 5 |
+
from utils.utils_vars import UtilsVars
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def setup_function():
|
| 9 |
+
UtilsVars.all_db_dfs = []
|
| 10 |
+
UtilsVars.all_db_dfs_names = []
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def test_process_ipaddressv4_data_keeps_only_mrbts_and_local_ip(monkeypatch):
|
| 14 |
+
dump_dfs = {
|
| 15 |
+
"IPADDRESSV4": pd.DataFrame(
|
| 16 |
+
{
|
| 17 |
+
" MRBTS ": [" 12345.0 ", "67890"],
|
| 18 |
+
"local Ip Addr": [" 10.1.1.1 ", "10.2.2.2"],
|
| 19 |
+
"extraColumn": ["ignored", "ignored"],
|
| 20 |
+
}
|
| 21 |
+
)
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
monkeypatch.setattr("queries.process_ipaddressv4._sheet_exists", lambda *args: True)
|
| 25 |
+
monkeypatch.setattr(
|
| 26 |
+
"queries.process_ipaddressv4.read_dump_excel", lambda *args, **kwargs: dump_dfs
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
df = process_ipaddressv4_data("dummy.xlsb")
|
| 30 |
+
|
| 31 |
+
assert list(df.columns) == ["MRBTS", "localIpAddr"]
|
| 32 |
+
assert list(df["MRBTS"]) == ["12345", "67890"]
|
| 33 |
+
assert list(df["localIpAddr"]) == ["10.1.1.1", "10.2.2.2"]
|
| 34 |
+
assert UtilsVars.all_db_dfs_names == ["IPADDRESSV4"]
|
| 35 |
+
assert UtilsVars.all_db_dfs[0].equals(df)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def test_process_ipaddressv4_data_skips_when_sheet_is_absent(monkeypatch):
|
| 39 |
+
monkeypatch.setattr("queries.process_ipaddressv4._sheet_exists", lambda *args: False)
|
| 40 |
+
|
| 41 |
+
df = process_ipaddressv4_data("dummy.xlsb")
|
| 42 |
+
|
| 43 |
+
assert list(df.columns) == ["MRBTS", "localIpAddr"]
|
| 44 |
+
assert df.empty
|
| 45 |
+
assert UtilsVars.all_db_dfs == []
|
| 46 |
+
assert UtilsVars.all_db_dfs_names == []
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def test_process_ipaddressv4_data_requires_local_ip_column(monkeypatch):
|
| 50 |
+
dump_dfs = {"IPADDRESSV4": pd.DataFrame({"MRBTS": ["12345"]})}
|
| 51 |
+
|
| 52 |
+
monkeypatch.setattr("queries.process_ipaddressv4._sheet_exists", lambda *args: True)
|
| 53 |
+
monkeypatch.setattr(
|
| 54 |
+
"queries.process_ipaddressv4.read_dump_excel", lambda *args, **kwargs: dump_dfs
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
with pytest.raises(ValueError, match="localIpAddr"):
|
| 58 |
+
process_ipaddressv4_data("dummy.xlsb")
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def test_process_all_tech_db_exports_ipaddressv4_as_final_extra_sheet(monkeypatch):
|
| 62 |
+
from queries import process_all_db
|
| 63 |
+
|
| 64 |
+
captured = {}
|
| 65 |
+
|
| 66 |
+
def fake_all_dbs(filepath):
|
| 67 |
+
UtilsVars.all_db_dfs = [pd.DataFrame({"source": ["base"]})]
|
| 68 |
+
UtilsVars.all_db_dfs_names = ["GSM"]
|
| 69 |
+
|
| 70 |
+
def fake_site_db():
|
| 71 |
+
UtilsVars.all_db_dfs.append(pd.DataFrame({"source": ["site"]}))
|
| 72 |
+
UtilsVars.all_db_dfs_names.append("SITE")
|
| 73 |
+
|
| 74 |
+
def fake_ipaddressv4(filepath):
|
| 75 |
+
UtilsVars.all_db_dfs.append(pd.DataFrame({"MRBTS": ["12345"]}))
|
| 76 |
+
UtilsVars.all_db_dfs_names.append("IPADDRESSV4")
|
| 77 |
+
|
| 78 |
+
def fake_convert_database_dfs(dfs, sheet_names):
|
| 79 |
+
captured["dfs"] = dfs
|
| 80 |
+
captured["sheet_names"] = sheet_names
|
| 81 |
+
return b"excel"
|
| 82 |
+
|
| 83 |
+
monkeypatch.setattr(process_all_db, "all_dbs", fake_all_dbs)
|
| 84 |
+
monkeypatch.setattr(process_all_db, "site_db", fake_site_db)
|
| 85 |
+
monkeypatch.setattr(process_all_db, "process_ipaddressv4_data", fake_ipaddressv4)
|
| 86 |
+
monkeypatch.setattr(
|
| 87 |
+
process_all_db, "convert_database_dfs", fake_convert_database_dfs
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
process_all_db.process_all_tech_db("dummy.xlsb")
|
| 91 |
+
|
| 92 |
+
assert UtilsVars.final_all_database == b"excel"
|
| 93 |
+
assert captured["sheet_names"] == ["GSM", "SITE", "IPADDRESSV4"]
|
| 94 |
+
assert len(captured["dfs"]) == 3
|