feat(dm): завершено построение DM-слоя (5 витрин) и исправлены баги денормализации

- Зачем:
  - предоставить студентам полный набор аналитических витрин с примерами различных паттернов (UPSERT, Full Rebuild, UNION ALL, двухуровневая агрегация).
- Что:
  - реализованы витрины: sales_report, route_performance, passenger_loyalty, airport_traffic, monthly_overview.
  - исправлен баг в route_performance_load.sql: добавлены JOIN к dim_airports и dim_airplanes для корректной денормализации атрибутов.
  - обновлен скрипт e2e_etl.sh: добавлена верификация всех 5 витрин и проверка бизнес-логики (load factor).
  - обновлены DAGи, тесты и главный DDL скрипт.
- Проверка:
  - автоматизированный прогон e2e_etl.sh через REST API Airflow.
This commit is contained in:
2026-03-04 22:55:16 +03:00
parent f68b055b00
commit 2228312b55
9 changed files with 342 additions and 32 deletions
+31 -7
View File
@@ -390,11 +390,18 @@ def test_bookings_dm_ddl_dag_structure():
expected_tasks = {
"apply_dm_sales_report_ddl",
"apply_dm_route_performance_ddl",
"apply_dm_passenger_loyalty_ddl",
"apply_dm_airport_traffic_ddl",
"apply_dm_monthly_overview_ddl",
}
assert expected_tasks.issubset(dag.task_dict.keys())
# На данном этапе только одна витрина
# В будущем: линейная цепочка из 5 задач
# Проверяем линейную цепочку
_assert_direct_edge(dag, "apply_dm_sales_report_ddl", "apply_dm_route_performance_ddl")
_assert_direct_edge(dag, "apply_dm_route_performance_ddl", "apply_dm_passenger_loyalty_ddl")
_assert_direct_edge(dag, "apply_dm_passenger_loyalty_ddl", "apply_dm_airport_traffic_ddl")
_assert_direct_edge(dag, "apply_dm_airport_traffic_ddl", "apply_dm_monthly_overview_ddl")
def test_bookings_to_gp_dm_dag_structure():
@@ -405,13 +412,30 @@ def test_bookings_to_gp_dm_dag_structure():
"start_dm",
"load_dm_sales_report",
"dq_dm_sales_report",
"load_dm_route_performance",
"dq_dm_route_performance",
"load_dm_passenger_loyalty",
"dq_dm_passenger_loyalty",
"load_dm_airport_traffic",
"dq_dm_airport_traffic",
"load_dm_monthly_overview",
"dq_dm_monthly_overview",
"finish_dm_summary",
}
assert expected_tasks.issubset(dag.task_dict.keys())
# Проверяем зависимости load -> dq
_assert_direct_edge(dag, "load_dm_sales_report", "dq_dm_sales_report")
marts = [
"sales_report",
"route_performance",
"passenger_loyalty",
"airport_traffic",
"monthly_overview",
]
# Проверяем, что start -> load -> dq -> finish
_assert_reachable(dag, "start_dm", "load_dm_sales_report")
_assert_reachable(dag, "dq_dm_sales_report", "finish_dm_summary")
for mart in marts:
# load -> dq
_assert_direct_edge(dag, f"load_dm_{mart}", f"dq_dm_{mart}")
# start -> load
_assert_reachable(dag, "start_dm", f"load_dm_{mart}")
# dq -> finish
_assert_reachable(dag, f"dq_dm_{mart}", "finish_dm_summary")