fix(superset): заменена нечитаемая гео-карта

- Зачем:
  - гео-блок дашборда должен показывать понятную метрику, единицы и сравнение стран.
- Что:
  - legacy world_map заменён на столбцы Top Countries by Events с tooltip и легендой.
  - синхронизирован экспорт дашборда и добавлены контрактные тесты.
  - обновлены документы и урок Superset по новому гео-блоку.
- Проверка:
  - uv run --with pytest pytest tests/test_superset_dashboard_config.py.
  - uv run python -m py_compile superset/create_dashboard.py tests/test_superset_dashboard_config.py.
  - jq empty superset/dashboards/ecommerce_analytics.zip.json.
This commit is contained in:
2026-07-04 22:05:56 +03:00
parent 0f435d70fd
commit 0c80e2438e
5 changed files with 389 additions and 78 deletions
+130
View File
@@ -0,0 +1,130 @@
import importlib.util
import json
from pathlib import Path
def load_dashboard_module():
module_path = Path(__file__).resolve().parents[1] / "superset" / "create_dashboard.py"
spec = importlib.util.spec_from_file_location("create_dashboard", module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def chart_config(module, slice_name):
return next(chart for chart in module.CHARTS_CONFIG if chart["slice_name"] == slice_name)
def exported_chart(exported_dashboard, slice_name):
return next(
chart["__Slice__"]
for chart in exported_dashboard["charts"]
if chart["__Slice__"]["slice_name"] == slice_name
)
def load_exported_dashboard():
export_path = Path(__file__).resolve().parents[1] / "superset" / "dashboards" / "ecommerce_analytics.zip.json"
return json.loads(export_path.read_text())
class FakeChart:
def __init__(self, chart_id, slice_name):
self.id = chart_id
self.slice_name = slice_name
def test_geo_chart_uses_readable_top_countries_bar_config():
module = load_dashboard_module()
geo_chart = chart_config(module, "🌍 Top Countries by Events")
assert geo_chart["viz_type"] == "echarts_timeseries_bar"
assert geo_chart["previous_slice_names"] == ["🌍 Geography Map"]
params = geo_chart["params"]
assert params["x_axis"] == "geo_country"
assert params["metrics"] == [
{
"expressionType": "SQL",
"sqlExpression": "COUNT(*)",
"label": "Events, pcs",
}
]
assert params["row_limit"] == 15
assert params["order_desc"] is True
assert params["sort_series_type"] == "sum"
assert params["show_legend"] is True
assert params["rich_tooltip"] is True
assert params["y_axis_title"] == "Events, pcs"
assert params["x_axis_title"] == "Country"
assert params["y_axis_format"] == ",d"
def test_exported_dashboard_uses_same_readable_geo_chart():
exported_dashboard = load_exported_dashboard()
geo_chart = exported_chart(exported_dashboard, "🌍 Top Countries by Events")
assert geo_chart["viz_type"] == "echarts_timeseries_bar"
params = json.loads(geo_chart["params"])
assert params["viz_type"] == "echarts_timeseries_bar"
assert params["x_axis"] == "geo_country"
assert params["metrics"][0]["label"] == "Events, pcs"
assert params["show_legend"] is True
assert params["rich_tooltip"] is True
assert params["y_axis_title"] == "Events, pcs"
position_json = json.loads(exported_dashboard["dashboards"][0]["__Dashboard__"]["position_json"])
assert position_json["CHART-7"]["meta"]["sliceName"] == "🌍 Top Countries by Events"
def test_exported_dashboard_chart_names_match_current_config():
module = load_dashboard_module()
exported_dashboard = load_exported_dashboard()
expected_names = [chart["slice_name"] for chart in module.CHARTS_CONFIG]
actual_names = [chart["__Slice__"]["slice_name"] for chart in exported_dashboard["charts"]]
assert actual_names == expected_names
def test_exported_dashboard_layout_matches_current_rows():
module = load_dashboard_module()
exported_dashboard = load_exported_dashboard()
position_json = json.loads(exported_dashboard["dashboards"][0]["__Dashboard__"]["position_json"])
chart_titles_by_id = {
component_id: component["meta"]["sliceName"]
for component_id, component in position_json.items()
if isinstance(component, dict) and component.get("type") == "CHART"
}
actual_rows = []
for row_id in position_json["GRID_ID"]["children"]:
row = []
for chart_component_id in position_json[row_id]["children"]:
chart_component = position_json[chart_component_id]
row.append(
(
chart_titles_by_id[chart_component_id],
chart_component["meta"]["width"],
)
)
actual_rows.append(row)
assert actual_rows == module.DASHBOARD_ROWS
def test_choose_chart_to_sync_prefers_current_name_and_marks_old_name_duplicate():
module = load_dashboard_module()
old_chart = FakeChart(7, "🌍 Geography Map")
current_chart = FakeChart(11, "🌍 Top Countries by Events")
selected, duplicates = module.choose_chart_to_sync(
[old_chart, current_chart],
"🌍 Top Countries by Events",
)
assert selected is current_chart
assert duplicates == [old_chart]