semantics
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
# @SEMANTICS: dataset_review, superset, link_parsing, context_recovery, partial_recovery
|
||||
# @PURPOSE: Recover dataset and dashboard context from Superset links while preserving explicit partial-recovery markers.
|
||||
# @LAYER: Infra
|
||||
# @RELATION: [CALLS] ->[backend.src.core.superset_client.SupersetClient:Class]
|
||||
# @RELATION: [DEPENDS_ON] ->[ImportedFilter]
|
||||
# @RELATION: [DEPENDS_ON] ->[ImportedFilter]
|
||||
# @RELATION: [DEPENDS_ON] ->[TemplateVariable]
|
||||
# @PRE: Superset link or dataset reference must be parseable enough to resolve an environment-scoped target resource.
|
||||
@@ -18,7 +18,7 @@ import json
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from typing import Any, Dict, List, Optional, Set, cast
|
||||
from urllib.parse import parse_qs, unquote, urlparse
|
||||
|
||||
from src.core.config_models import Environment
|
||||
@@ -26,6 +26,8 @@ from src.core.logger import belief_scope, logger
|
||||
from src.core.superset_client import SupersetClient
|
||||
# [/DEF:SupersetContextExtractor.imports:Block]
|
||||
|
||||
logger = cast(Any, logger)
|
||||
|
||||
|
||||
# [DEF:SupersetParsedContext:Class]
|
||||
# @COMPLEXITY: 2
|
||||
@@ -42,13 +44,15 @@ class SupersetParsedContext:
|
||||
imported_filters: List[Dict[str, Any]] = field(default_factory=list)
|
||||
unresolved_references: List[str] = field(default_factory=list)
|
||||
partial_recovery: bool = False
|
||||
|
||||
|
||||
# [/DEF:SupersetParsedContext:Class]
|
||||
|
||||
|
||||
# [DEF:SupersetContextExtractor:Class]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Parse supported Superset URLs and recover canonical dataset/dashboard references for review-session intake.
|
||||
# @RELATION: [CALLS] ->[backend.src.core.superset_client.SupersetClient]
|
||||
# @RELATION: [DEPENDS_ON] ->[Environment]
|
||||
# @PRE: constructor receives a configured environment with a usable Superset base URL.
|
||||
# @POST: extractor instance is ready to parse links against one Superset environment.
|
||||
# @SIDE_EFFECT: downstream parse operations may call Superset APIs through SupersetClient.
|
||||
@@ -56,15 +60,18 @@ class SupersetContextExtractor:
|
||||
# [DEF:SupersetContextExtractor.__init__:Function]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Bind extractor to one Superset environment and client instance.
|
||||
def __init__(self, environment: Environment, client: Optional[SupersetClient] = None) -> None:
|
||||
def __init__(
|
||||
self, environment: Environment, client: Optional[SupersetClient] = None
|
||||
) -> None:
|
||||
self.environment = environment
|
||||
self.client = client or SupersetClient(environment)
|
||||
|
||||
# [/DEF:SupersetContextExtractor.__init__:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor.parse_superset_link:Function]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Extract candidate identifiers and query state from supported Superset URLs.
|
||||
# @RELATION: [CALLS] ->[backend.src.core.superset_client.SupersetClient]
|
||||
# @RELATION: [CALLS] ->[SupersetClient.get_dashboard_detail]
|
||||
# @PRE: link is a non-empty Superset URL compatible with the configured environment.
|
||||
# @POST: returns resolved dataset/dashboard context, preserving explicit partial-recovery state if some identifiers cannot be confirmed.
|
||||
# @SIDE_EFFECT: may issue Superset API reads to resolve dataset references from dashboard or chart URLs.
|
||||
@@ -115,12 +122,16 @@ class SupersetContextExtractor:
|
||||
resource_type = "dashboard"
|
||||
partial_recovery = True
|
||||
dataset_ref = f"dashboard_permalink:{dashboard_permalink_key}"
|
||||
unresolved_references.append("dashboard_permalink_dataset_binding_unresolved")
|
||||
unresolved_references.append(
|
||||
"dashboard_permalink_dataset_binding_unresolved"
|
||||
)
|
||||
logger.reason(
|
||||
"Resolving dashboard permalink state from Superset",
|
||||
extra={"permalink_key": dashboard_permalink_key},
|
||||
)
|
||||
permalink_payload = self.client.get_dashboard_permalink_state(dashboard_permalink_key)
|
||||
permalink_payload = self.client.get_dashboard_permalink_state(
|
||||
dashboard_permalink_key
|
||||
)
|
||||
permalink_state = (
|
||||
permalink_payload.get("state", permalink_payload)
|
||||
if isinstance(permalink_payload, dict)
|
||||
@@ -137,8 +148,12 @@ class SupersetContextExtractor:
|
||||
"Extracted native filters from permalink dataMask",
|
||||
extra={"filter_count": len(data_mask)},
|
||||
)
|
||||
resolved_dashboard_id = self._extract_dashboard_id_from_state(permalink_state)
|
||||
resolved_chart_id = self._extract_chart_id_from_state(permalink_state)
|
||||
resolved_dashboard_id = self._extract_dashboard_id_from_state(
|
||||
permalink_state
|
||||
)
|
||||
resolved_chart_id = self._extract_chart_id_from_state(
|
||||
permalink_state
|
||||
)
|
||||
if resolved_dashboard_id is not None:
|
||||
dashboard_id = resolved_dashboard_id
|
||||
unresolved_references = [
|
||||
@@ -146,10 +161,12 @@ class SupersetContextExtractor:
|
||||
for item in unresolved_references
|
||||
if item != "dashboard_permalink_dataset_binding_unresolved"
|
||||
]
|
||||
dataset_id, unresolved_references = self._recover_dataset_binding_from_dashboard(
|
||||
dashboard_id=dashboard_id,
|
||||
dataset_ref=dataset_ref,
|
||||
unresolved_references=unresolved_references,
|
||||
dataset_id, unresolved_references = (
|
||||
self._recover_dataset_binding_from_dashboard(
|
||||
dashboard_id=dashboard_id,
|
||||
dataset_ref=dataset_ref,
|
||||
unresolved_references=unresolved_references,
|
||||
)
|
||||
)
|
||||
if dataset_id is not None:
|
||||
dataset_ref = f"dataset:{dataset_id}"
|
||||
@@ -162,19 +179,30 @@ class SupersetContextExtractor:
|
||||
]
|
||||
try:
|
||||
chart_payload = self.client.get_chart(chart_id)
|
||||
chart_data = chart_payload.get("result", chart_payload) if isinstance(chart_payload, dict) else {}
|
||||
chart_data = (
|
||||
chart_payload.get("result", chart_payload)
|
||||
if isinstance(chart_payload, dict)
|
||||
else {}
|
||||
)
|
||||
datasource_id = chart_data.get("datasource_id")
|
||||
if datasource_id is not None:
|
||||
dataset_id = int(datasource_id)
|
||||
dataset_ref = f"dataset:{dataset_id}"
|
||||
logger.reason(
|
||||
"Recovered dataset reference from permalink chart context",
|
||||
extra={"chart_id": chart_id, "dataset_id": dataset_id},
|
||||
extra={
|
||||
"chart_id": chart_id,
|
||||
"dataset_id": dataset_id,
|
||||
},
|
||||
)
|
||||
else:
|
||||
unresolved_references.append("chart_dataset_binding_unresolved")
|
||||
unresolved_references.append(
|
||||
"chart_dataset_binding_unresolved"
|
||||
)
|
||||
except Exception as exc:
|
||||
unresolved_references.append("chart_dataset_binding_unresolved")
|
||||
unresolved_references.append(
|
||||
"chart_dataset_binding_unresolved"
|
||||
)
|
||||
logger.explore(
|
||||
"Chart lookup failed during permalink recovery",
|
||||
extra={"chart_id": chart_id, "error": str(exc)},
|
||||
@@ -186,19 +214,25 @@ class SupersetContextExtractor:
|
||||
)
|
||||
elif dashboard_id is not None or dashboard_ref is not None:
|
||||
resource_type = "dashboard"
|
||||
resolved_dashboard_ref = dashboard_id if dashboard_id is not None else dashboard_ref
|
||||
resolved_dashboard_ref = (
|
||||
dashboard_id if dashboard_id is not None else dashboard_ref
|
||||
)
|
||||
if resolved_dashboard_ref is None:
|
||||
raise ValueError("Dashboard reference could not be resolved")
|
||||
logger.reason(
|
||||
"Resolving dashboard-bound dataset from Superset",
|
||||
extra={"dashboard_ref": resolved_dashboard_ref},
|
||||
)
|
||||
|
||||
|
||||
# Resolve dashboard detail first — handles both numeric ID and slug,
|
||||
# ensuring dashboard_id is available for the native_filters_key fetch below.
|
||||
dashboard_detail = self.client.get_dashboard_detail(resolved_dashboard_ref)
|
||||
dashboard_detail = self.client.get_dashboard_detail(
|
||||
resolved_dashboard_ref
|
||||
)
|
||||
resolved_dashboard_id = dashboard_detail.get("id")
|
||||
if resolved_dashboard_id is not None:
|
||||
dashboard_id = int(resolved_dashboard_id)
|
||||
|
||||
|
||||
# Check for native_filters_key in query params and fetch filter state.
|
||||
# This must run AFTER dashboard_id is resolved from slug above.
|
||||
native_filters_key = query_params.get("native_filters_key", [None])[0]
|
||||
@@ -206,7 +240,10 @@ class SupersetContextExtractor:
|
||||
try:
|
||||
logger.reason(
|
||||
"Fetching native filter state from Superset",
|
||||
extra={"dashboard_id": dashboard_id, "filter_key": native_filters_key},
|
||||
extra={
|
||||
"dashboard_id": dashboard_id,
|
||||
"filter_key": native_filters_key,
|
||||
},
|
||||
)
|
||||
extracted = self.client.extract_native_filters_from_key(
|
||||
dashboard_id, native_filters_key
|
||||
@@ -221,14 +258,21 @@ class SupersetContextExtractor:
|
||||
else:
|
||||
logger.explore(
|
||||
"Native filter state returned empty dataMask",
|
||||
extra={"dashboard_id": dashboard_id, "filter_key": native_filters_key},
|
||||
extra={
|
||||
"dashboard_id": dashboard_id,
|
||||
"filter_key": native_filters_key,
|
||||
},
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.explore(
|
||||
"Failed to fetch native filter state from Superset",
|
||||
extra={"dashboard_id": dashboard_id, "filter_key": native_filters_key, "error": str(exc)},
|
||||
extra={
|
||||
"dashboard_id": dashboard_id,
|
||||
"filter_key": native_filters_key,
|
||||
"error": str(exc),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
datasets = dashboard_detail.get("datasets") or []
|
||||
if datasets:
|
||||
first_dataset = datasets[0]
|
||||
@@ -280,7 +324,10 @@ class SupersetContextExtractor:
|
||||
)
|
||||
logger.reason(
|
||||
"Canonicalized dataset reference from dataset detail",
|
||||
extra={"dataset_ref": dataset_ref, "dataset_id": dataset_id},
|
||||
extra={
|
||||
"dataset_ref": dataset_ref,
|
||||
"dataset_id": dataset_id,
|
||||
},
|
||||
)
|
||||
except Exception as exc:
|
||||
partial_recovery = True
|
||||
@@ -316,17 +363,20 @@ class SupersetContextExtractor:
|
||||
},
|
||||
)
|
||||
return result
|
||||
|
||||
# [/DEF:SupersetContextExtractor.parse_superset_link:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor.recover_imported_filters:Function]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Build imported filter entries from URL state and Superset-side saved context.
|
||||
# @RELATION: [CALLS] ->[backend.src.core.superset_client.SupersetClient]
|
||||
# @RELATION: [CALLS] ->[SupersetClient.get_dashboard]
|
||||
# @PRE: parsed_context comes from a successful Superset link parse for one environment.
|
||||
# @POST: returns explicit recovered and partial filter entries with preserved provenance and confirmation requirements.
|
||||
# @SIDE_EFFECT: may issue Superset reads for dashboard metadata enrichment.
|
||||
# @DATA_CONTRACT: Input[SupersetParsedContext] -> Output[List[Dict[str,Any]]]
|
||||
def recover_imported_filters(self, parsed_context: SupersetParsedContext) -> List[Dict[str, Any]]:
|
||||
def recover_imported_filters(
|
||||
self, parsed_context: SupersetParsedContext
|
||||
) -> List[Dict[str, Any]]:
|
||||
with belief_scope("SupersetContextExtractor.recover_imported_filters"):
|
||||
recovered_filters: List[Dict[str, Any]] = []
|
||||
seen_filter_keys: Set[str] = set()
|
||||
@@ -349,22 +399,46 @@ class SupersetContextExtractor:
|
||||
return
|
||||
|
||||
existing = recovered_filters[existing_index]
|
||||
if existing.get("display_name") in {None, "", existing.get("filter_name")} and candidate.get("display_name"):
|
||||
if existing.get("display_name") in {
|
||||
None,
|
||||
"",
|
||||
existing.get("filter_name"),
|
||||
} and candidate.get("display_name"):
|
||||
existing["display_name"] = candidate["display_name"]
|
||||
if existing.get("raw_value") is None and candidate.get("raw_value") is not None:
|
||||
if (
|
||||
existing.get("raw_value") is None
|
||||
and candidate.get("raw_value") is not None
|
||||
):
|
||||
existing["raw_value"] = candidate["raw_value"]
|
||||
existing["confidence_state"] = candidate.get("confidence_state", "imported")
|
||||
existing["requires_confirmation"] = candidate.get("requires_confirmation", False)
|
||||
existing["recovery_status"] = candidate.get("recovery_status", "recovered")
|
||||
existing["confidence_state"] = candidate.get(
|
||||
"confidence_state", "imported"
|
||||
)
|
||||
existing["requires_confirmation"] = candidate.get(
|
||||
"requires_confirmation", False
|
||||
)
|
||||
existing["recovery_status"] = candidate.get(
|
||||
"recovery_status", "recovered"
|
||||
)
|
||||
existing["source"] = candidate.get("source", existing.get("source"))
|
||||
if existing.get("normalized_value") is None and candidate.get("normalized_value") is not None:
|
||||
existing["normalized_value"] = deepcopy(candidate["normalized_value"])
|
||||
if existing.get("notes") and candidate.get("notes") and candidate["notes"] not in existing["notes"]:
|
||||
existing["notes"] = f'{existing["notes"]}; {candidate["notes"]}'
|
||||
if (
|
||||
existing.get("normalized_value") is None
|
||||
and candidate.get("normalized_value") is not None
|
||||
):
|
||||
existing["normalized_value"] = deepcopy(
|
||||
candidate["normalized_value"]
|
||||
)
|
||||
if (
|
||||
existing.get("notes")
|
||||
and candidate.get("notes")
|
||||
and candidate["notes"] not in existing["notes"]
|
||||
):
|
||||
existing["notes"] = f"{existing['notes']}; {candidate['notes']}"
|
||||
|
||||
if parsed_context.dashboard_id is not None:
|
||||
try:
|
||||
dashboard_payload = self.client.get_dashboard(parsed_context.dashboard_id)
|
||||
dashboard_payload = self.client.get_dashboard(
|
||||
parsed_context.dashboard_id
|
||||
)
|
||||
dashboard_record = (
|
||||
dashboard_payload.get("result", dashboard_payload)
|
||||
if isinstance(dashboard_payload, dict)
|
||||
@@ -376,7 +450,9 @@ class SupersetContextExtractor:
|
||||
if not isinstance(json_metadata, dict):
|
||||
json_metadata = {}
|
||||
|
||||
native_filter_configuration = json_metadata.get("native_filter_configuration") or []
|
||||
native_filter_configuration = (
|
||||
json_metadata.get("native_filter_configuration") or []
|
||||
)
|
||||
default_filters = json_metadata.get("default_filters") or {}
|
||||
if isinstance(default_filters, str) and default_filters.strip():
|
||||
try:
|
||||
@@ -400,7 +476,9 @@ class SupersetContextExtractor:
|
||||
if not filter_name:
|
||||
continue
|
||||
|
||||
display_name = item.get("label") or item.get("name") or filter_name
|
||||
display_name = (
|
||||
item.get("label") or item.get("name") or filter_name
|
||||
)
|
||||
filter_id = str(item.get("id") or "").strip()
|
||||
|
||||
default_value = None
|
||||
@@ -413,7 +491,9 @@ class SupersetContextExtractor:
|
||||
"display_name": display_name,
|
||||
"raw_value": default_value,
|
||||
"source": "superset_native",
|
||||
"recovery_status": "recovered" if default_value is not None else "partial",
|
||||
"recovery_status": "recovered"
|
||||
if default_value is not None
|
||||
else "partial",
|
||||
"requires_confirmation": default_value is None,
|
||||
"notes": "Recovered from Superset dashboard native filter configuration",
|
||||
},
|
||||
@@ -445,7 +525,9 @@ class SupersetContextExtractor:
|
||||
default_source="superset_url",
|
||||
default_note="Recovered from Superset URL state",
|
||||
)
|
||||
metadata_match = metadata_filters_by_id.get(normalized["filter_name"].strip().lower())
|
||||
metadata_match = metadata_filters_by_id.get(
|
||||
normalized["filter_name"].strip().lower()
|
||||
)
|
||||
if metadata_match is not None:
|
||||
normalized["filter_name"] = metadata_match["filter_name"]
|
||||
normalized["display_name"] = metadata_match["display_name"]
|
||||
@@ -517,6 +599,7 @@ class SupersetContextExtractor:
|
||||
},
|
||||
)
|
||||
return recovered_filters
|
||||
|
||||
# [/DEF:SupersetContextExtractor.recover_imported_filters:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor.discover_template_variables:Function]
|
||||
@@ -527,12 +610,16 @@ class SupersetContextExtractor:
|
||||
# @POST: returns deduplicated explicit variable records without executing Jinja or fabricating runtime values.
|
||||
# @SIDE_EFFECT: none.
|
||||
# @DATA_CONTRACT: Input[dataset_payload:Dict[str,Any]] -> Output[List[Dict[str,Any]]]
|
||||
def discover_template_variables(self, dataset_payload: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
def discover_template_variables(
|
||||
self, dataset_payload: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
with belief_scope("SupersetContextExtractor.discover_template_variables"):
|
||||
discovered: List[Dict[str, Any]] = []
|
||||
seen_variable_names: Set[str] = set()
|
||||
|
||||
for expression_source in self._collect_query_bearing_expressions(dataset_payload):
|
||||
for expression_source in self._collect_query_bearing_expressions(
|
||||
dataset_payload
|
||||
):
|
||||
for filter_match in re.finditer(
|
||||
r"filter_values\(\s*['\"]([^'\"]+)['\"]\s*\)",
|
||||
expression_source,
|
||||
@@ -570,11 +657,16 @@ class SupersetContextExtractor:
|
||||
default_value=self._normalize_default_literal(default_literal),
|
||||
)
|
||||
|
||||
for jinja_match in re.finditer(r"\{\{\s*(.*?)\s*\}\}", expression_source, flags=re.DOTALL):
|
||||
for jinja_match in re.finditer(
|
||||
r"\{\{\s*(.*?)\s*\}\}", expression_source, flags=re.DOTALL
|
||||
):
|
||||
expression = str(jinja_match.group(1) or "").strip()
|
||||
if not expression:
|
||||
continue
|
||||
if any(token in expression for token in ("filter_values(", "url_param(", "get_filters(")):
|
||||
if any(
|
||||
token in expression
|
||||
for token in ("filter_values(", "url_param(", "get_filters(")
|
||||
):
|
||||
continue
|
||||
variable_name = self._extract_primary_jinja_identifier(expression)
|
||||
if not variable_name:
|
||||
@@ -584,7 +676,9 @@ class SupersetContextExtractor:
|
||||
seen_variable_names=seen_variable_names,
|
||||
variable_name=variable_name,
|
||||
expression_source=expression_source,
|
||||
variable_kind="derived" if "." in expression or "|" in expression else "parameter",
|
||||
variable_kind="derived"
|
||||
if "." in expression or "|" in expression
|
||||
else "parameter",
|
||||
is_required=True,
|
||||
default_value=None,
|
||||
)
|
||||
@@ -598,12 +692,15 @@ class SupersetContextExtractor:
|
||||
},
|
||||
)
|
||||
return discovered
|
||||
|
||||
# [/DEF:SupersetContextExtractor.discover_template_variables:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor.build_recovery_summary:Function]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Summarize recovered, partial, and unresolved context for session state and UX.
|
||||
def build_recovery_summary(self, parsed_context: SupersetParsedContext) -> Dict[str, Any]:
|
||||
def build_recovery_summary(
|
||||
self, parsed_context: SupersetParsedContext
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"dataset_ref": parsed_context.dataset_ref,
|
||||
"dataset_id": parsed_context.dataset_id,
|
||||
@@ -613,12 +710,15 @@ class SupersetContextExtractor:
|
||||
"unresolved_references": list(parsed_context.unresolved_references),
|
||||
"imported_filter_count": len(parsed_context.imported_filters),
|
||||
}
|
||||
|
||||
# [/DEF:SupersetContextExtractor.build_recovery_summary:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_numeric_identifier:Function]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Extract a numeric identifier from a REST-like Superset URL path.
|
||||
def _extract_numeric_identifier(self, path_parts: List[str], resource_name: str) -> Optional[int]:
|
||||
def _extract_numeric_identifier(
|
||||
self, path_parts: List[str], resource_name: str
|
||||
) -> Optional[int]:
|
||||
if resource_name not in path_parts:
|
||||
return None
|
||||
try:
|
||||
@@ -633,6 +733,7 @@ class SupersetContextExtractor:
|
||||
if not candidate.isdigit():
|
||||
return None
|
||||
return int(candidate)
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_numeric_identifier:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_dashboard_reference:Function]
|
||||
@@ -653,6 +754,7 @@ class SupersetContextExtractor:
|
||||
if not candidate or candidate == "p":
|
||||
return None
|
||||
return candidate
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_dashboard_reference:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_dashboard_permalink_key:Function]
|
||||
@@ -674,6 +776,7 @@ class SupersetContextExtractor:
|
||||
if permalink_marker != "p" or not permalink_key:
|
||||
return None
|
||||
return permalink_key
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_dashboard_permalink_key:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_dashboard_id_from_state:Function]
|
||||
@@ -684,6 +787,7 @@ class SupersetContextExtractor:
|
||||
payload=state,
|
||||
candidate_keys={"dashboardId", "dashboard_id", "dashboard_id_value"},
|
||||
)
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_dashboard_id_from_state:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_chart_id_from_state:Function]
|
||||
@@ -694,12 +798,16 @@ class SupersetContextExtractor:
|
||||
payload=state,
|
||||
candidate_keys={"slice_id", "sliceId", "chartId", "chart_id"},
|
||||
)
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_chart_id_from_state:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._search_nested_numeric_key:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Recursively search nested dict/list payloads for the first numeric value under a candidate key set.
|
||||
def _search_nested_numeric_key(self, payload: Any, candidate_keys: Set[str]) -> Optional[int]:
|
||||
# @RELATION: [DEPENDS_ON] ->[SupersetContextExtractor.parse_superset_link]
|
||||
def _search_nested_numeric_key(
|
||||
self, payload: Any, candidate_keys: Set[str]
|
||||
) -> Optional[int]:
|
||||
if isinstance(payload, dict):
|
||||
for key, value in payload.items():
|
||||
if key in candidate_keys:
|
||||
@@ -717,11 +825,13 @@ class SupersetContextExtractor:
|
||||
if found is not None:
|
||||
return found
|
||||
return None
|
||||
|
||||
# [/DEF:SupersetContextExtractor._search_nested_numeric_key:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._recover_dataset_binding_from_dashboard:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Recover a dataset binding from resolved dashboard context while preserving explicit unresolved markers.
|
||||
# @RELATION: [CALLS] ->[SupersetClient.get_dashboard_detail]
|
||||
def _recover_dataset_binding_from_dashboard(
|
||||
self,
|
||||
dashboard_id: int,
|
||||
@@ -744,7 +854,10 @@ class SupersetContextExtractor:
|
||||
"dataset_ref": dataset_ref,
|
||||
},
|
||||
)
|
||||
if len(datasets) > 1 and "multiple_dashboard_datasets" not in unresolved_references:
|
||||
if (
|
||||
len(datasets) > 1
|
||||
and "multiple_dashboard_datasets" not in unresolved_references
|
||||
):
|
||||
unresolved_references.append("multiple_dashboard_datasets")
|
||||
return resolved_dataset, unresolved_references
|
||||
if "dashboard_dataset_id_missing" not in unresolved_references:
|
||||
@@ -754,6 +867,7 @@ class SupersetContextExtractor:
|
||||
if "dashboard_dataset_binding_missing" not in unresolved_references:
|
||||
unresolved_references.append("dashboard_dataset_binding_missing")
|
||||
return None, unresolved_references
|
||||
|
||||
# [/DEF:SupersetContextExtractor._recover_dataset_binding_from_dashboard:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._decode_query_state:Function]
|
||||
@@ -777,12 +891,15 @@ class SupersetContextExtractor:
|
||||
)
|
||||
query_state[key] = decoded_value
|
||||
return query_state
|
||||
|
||||
# [/DEF:SupersetContextExtractor._decode_query_state:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_imported_filters:Function]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Normalize imported filters from decoded query state without fabricating missing values.
|
||||
def _extract_imported_filters(self, query_state: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
def _extract_imported_filters(
|
||||
self, query_state: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
imported_filters: List[Dict[str, Any]] = []
|
||||
|
||||
native_filters_payload = query_state.get("native_filters")
|
||||
@@ -800,7 +917,8 @@ class SupersetContextExtractor:
|
||||
if item.get("column") and ("value" in item or "val" in item):
|
||||
direct_clause = {
|
||||
"col": item.get("column"),
|
||||
"op": item.get("op") or ("IN" if isinstance(item.get("value"), list) else "=="),
|
||||
"op": item.get("op")
|
||||
or ("IN" if isinstance(item.get("value"), list) else "=="),
|
||||
"val": item.get("val", item.get("value")),
|
||||
}
|
||||
imported_filters.append(
|
||||
@@ -809,7 +927,9 @@ class SupersetContextExtractor:
|
||||
"raw_value": item.get("value"),
|
||||
"display_name": item.get("label") or item.get("name"),
|
||||
"normalized_value": {
|
||||
"filter_clauses": [direct_clause] if isinstance(direct_clause, dict) else [],
|
||||
"filter_clauses": [direct_clause]
|
||||
if isinstance(direct_clause, dict)
|
||||
else [],
|
||||
"extra_form_data": {},
|
||||
"value_origin": "native_filters",
|
||||
},
|
||||
@@ -834,7 +954,9 @@ class SupersetContextExtractor:
|
||||
raw_value = None
|
||||
normalized_value = {
|
||||
"filter_clauses": [],
|
||||
"extra_form_data": deepcopy(extra_form_data) if isinstance(extra_form_data, dict) else {},
|
||||
"extra_form_data": deepcopy(extra_form_data)
|
||||
if isinstance(extra_form_data, dict)
|
||||
else {},
|
||||
"value_origin": "unresolved",
|
||||
}
|
||||
|
||||
@@ -868,10 +990,17 @@ class SupersetContextExtractor:
|
||||
# If still no value, try extraFormData directly for time_range, time_grain, etc.
|
||||
if raw_value is None and isinstance(extra_form_data, dict):
|
||||
# Common Superset filter fields
|
||||
for field in ["time_range", "time_grain_sqla", "time_column", "granularity"]:
|
||||
for field in [
|
||||
"time_range",
|
||||
"time_grain_sqla",
|
||||
"time_column",
|
||||
"granularity",
|
||||
]:
|
||||
if field in extra_form_data:
|
||||
raw_value = extra_form_data[field]
|
||||
normalized_value["value_origin"] = f"extra_form_data.{field}"
|
||||
normalized_value["value_origin"] = (
|
||||
f"extra_form_data.{field}"
|
||||
)
|
||||
break
|
||||
|
||||
imported_filters.append(
|
||||
@@ -881,7 +1010,9 @@ class SupersetContextExtractor:
|
||||
"display_name": display_name,
|
||||
"normalized_value": normalized_value,
|
||||
"source": "superset_permalink",
|
||||
"recovery_status": "recovered" if raw_value is not None else "partial",
|
||||
"recovery_status": "recovered"
|
||||
if raw_value is not None
|
||||
else "partial",
|
||||
"requires_confirmation": raw_value is None,
|
||||
"notes": "Recovered from Superset dashboard permalink state",
|
||||
}
|
||||
@@ -901,7 +1032,9 @@ class SupersetContextExtractor:
|
||||
raw_value = None
|
||||
normalized_value = {
|
||||
"filter_clauses": [],
|
||||
"extra_form_data": deepcopy(extra_form_data) if isinstance(extra_form_data, dict) else {},
|
||||
"extra_form_data": deepcopy(extra_form_data)
|
||||
if isinstance(extra_form_data, dict)
|
||||
else {},
|
||||
"value_origin": "unresolved",
|
||||
}
|
||||
|
||||
@@ -935,10 +1068,17 @@ class SupersetContextExtractor:
|
||||
# If still no value, try extraFormData directly for time_range, time_grain, etc.
|
||||
if raw_value is None and isinstance(extra_form_data, dict):
|
||||
# Common Superset filter fields
|
||||
for field in ["time_range", "time_grain_sqla", "time_column", "granularity"]:
|
||||
for field in [
|
||||
"time_range",
|
||||
"time_grain_sqla",
|
||||
"time_column",
|
||||
"granularity",
|
||||
]:
|
||||
if field in extra_form_data:
|
||||
raw_value = extra_form_data[field]
|
||||
normalized_value["value_origin"] = f"extra_form_data.{field}"
|
||||
normalized_value["value_origin"] = (
|
||||
f"extra_form_data.{field}"
|
||||
)
|
||||
break
|
||||
|
||||
imported_filters.append(
|
||||
@@ -948,7 +1088,9 @@ class SupersetContextExtractor:
|
||||
"display_name": display_name,
|
||||
"normalized_value": normalized_value,
|
||||
"source": "superset_native_filters_key",
|
||||
"recovery_status": "recovered" if raw_value is not None else "partial",
|
||||
"recovery_status": "recovered"
|
||||
if raw_value is not None
|
||||
else "partial",
|
||||
"requires_confirmation": raw_value is None,
|
||||
"notes": "Recovered from Superset native_filters_key state",
|
||||
}
|
||||
@@ -960,7 +1102,9 @@ class SupersetContextExtractor:
|
||||
for index, item in enumerate(extra_filters):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
filter_name = item.get("col") or item.get("column") or f"extra_filter_{index}"
|
||||
filter_name = (
|
||||
item.get("col") or item.get("column") or f"extra_filter_{index}"
|
||||
)
|
||||
imported_filters.append(
|
||||
{
|
||||
"filter_name": str(filter_name),
|
||||
@@ -981,6 +1125,7 @@ class SupersetContextExtractor:
|
||||
)
|
||||
|
||||
return imported_filters
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_imported_filters:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._normalize_imported_filter_payload:Function]
|
||||
@@ -996,15 +1141,24 @@ class SupersetContextExtractor:
|
||||
if "raw_value" not in payload and "value" in payload:
|
||||
raw_value = payload.get("value")
|
||||
|
||||
recovery_status = str(
|
||||
payload.get("recovery_status")
|
||||
or ("recovered" if raw_value is not None else "partial")
|
||||
).strip().lower()
|
||||
recovery_status = (
|
||||
str(
|
||||
payload.get("recovery_status")
|
||||
or ("recovered" if raw_value is not None else "partial")
|
||||
)
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
requires_confirmation = bool(
|
||||
payload.get("requires_confirmation", raw_value is None or recovery_status != "recovered")
|
||||
payload.get(
|
||||
"requires_confirmation",
|
||||
raw_value is None or recovery_status != "recovered",
|
||||
)
|
||||
)
|
||||
return {
|
||||
"filter_name": str(payload.get("filter_name") or "unresolved_filter").strip(),
|
||||
"filter_name": str(
|
||||
payload.get("filter_name") or "unresolved_filter"
|
||||
).strip(),
|
||||
"display_name": payload.get("display_name"),
|
||||
"raw_value": raw_value,
|
||||
"normalized_value": payload.get("normalized_value"),
|
||||
@@ -1014,13 +1168,16 @@ class SupersetContextExtractor:
|
||||
"recovery_status": recovery_status,
|
||||
"notes": str(payload.get("notes") or default_note),
|
||||
}
|
||||
|
||||
# [/DEF:SupersetContextExtractor._normalize_imported_filter_payload:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._collect_query_bearing_expressions:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Collect SQL and expression-bearing dataset fields for deterministic template-variable discovery.
|
||||
# @RELATION: [DEPENDS_ON] ->[SupersetContextExtractor.discover_template_variables]
|
||||
def _collect_query_bearing_expressions(self, dataset_payload: Dict[str, Any]) -> List[str]:
|
||||
def _collect_query_bearing_expressions(
|
||||
self, dataset_payload: Dict[str, Any]
|
||||
) -> List[str]:
|
||||
expressions: List[str] = []
|
||||
|
||||
def append_expression(candidate: Any) -> None:
|
||||
@@ -1055,6 +1212,7 @@ class SupersetContextExtractor:
|
||||
append_expression(column.get("expression"))
|
||||
|
||||
return expressions
|
||||
|
||||
# [/DEF:SupersetContextExtractor._collect_query_bearing_expressions:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._append_template_variable:Function]
|
||||
@@ -1087,6 +1245,7 @@ class SupersetContextExtractor:
|
||||
"mapping_status": "unmapped",
|
||||
}
|
||||
)
|
||||
|
||||
# [/DEF:SupersetContextExtractor._append_template_variable:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._extract_primary_jinja_identifier:Function]
|
||||
@@ -1100,6 +1259,7 @@ class SupersetContextExtractor:
|
||||
if candidate in {"if", "else", "for", "set", "True", "False", "none", "None"}:
|
||||
return None
|
||||
return candidate
|
||||
|
||||
# [/DEF:SupersetContextExtractor._extract_primary_jinja_identifier:Function]
|
||||
|
||||
# [DEF:SupersetContextExtractor._normalize_default_literal:Function]
|
||||
@@ -1110,9 +1270,8 @@ class SupersetContextExtractor:
|
||||
if not normalized_literal:
|
||||
return None
|
||||
if (
|
||||
(normalized_literal.startswith("'") and normalized_literal.endswith("'"))
|
||||
or (normalized_literal.startswith('"') and normalized_literal.endswith('"'))
|
||||
):
|
||||
normalized_literal.startswith("'") and normalized_literal.endswith("'")
|
||||
) or (normalized_literal.startswith('"') and normalized_literal.endswith('"')):
|
||||
return normalized_literal[1:-1]
|
||||
lowered = normalized_literal.lower()
|
||||
if lowered in {"true", "false"}:
|
||||
@@ -1126,7 +1285,10 @@ class SupersetContextExtractor:
|
||||
return float(normalized_literal)
|
||||
except ValueError:
|
||||
return normalized_literal
|
||||
|
||||
# [/DEF:SupersetContextExtractor._normalize_default_literal:Function]
|
||||
|
||||
|
||||
# [/DEF:SupersetContextExtractor:Class]
|
||||
|
||||
# [/DEF:SupersetContextExtractor:Module]
|
||||
# [/DEF:SupersetContextExtractor:Module]
|
||||
|
||||
Reference in New Issue
Block a user